/* eslint-disable no-undef */
import Store from "../store";
import { replaceHtml, getObjType, chatatABC, luckysheetactiveCell, numFormat, ABCatNum, nameStyleConvert, doRectanglesOverlap, getDPI as getDpi } from "../utils/util";
import { getSheetIndex, getluckysheet_select_save, getluckysheetfile, getRangetxt } from "../methods/get";
import locale from "../locale/locale";
import method from './method';
import formula from './formula';
import func_methods from "./func_methods";
import tooltip from "./tooltip";
import json from "./json";
import editor from "./editor";
import cleargridelement from './cleargridelement';
import { genarate, update } from './format';
import { initsetcellValue, setAccuracy, setcellvalue } from "./setdata";
import { orderbydata } from "./sort";
import { getMaxColumnLen, getMaxRowLen, rowlenByRange } from "./getRowlen";
import { getdatabyselection, getcellvalue, getHorizontalFreezenCount } from "./getdata";
import { luckysheetrefreshgrid, jfrefreshgrid, jfrefreshgrid_rhcw } from "./refresh";
import { luckysheetDeleteCell, luckysheetextendtable, luckysheetdeletetable } from "./extend";
import { isRealNull, valueIsError, isRealNum, isEditMode, hasPartMC } from "./validate";
import { isdatetime, diff } from "./datecontroll";
import { getBorderInfoCompute } from './border';
import { luckysheetDrawMain } from './draw';
import pivotTable from '../controllers/pivotTable';
import server from "../controllers/server";
import menuButton from '../controllers/menuButton';
import selection from "../controllers/selection";
import luckysheetConfigsetting from "../controllers/luckysheetConfigsetting";
import luckysheetFreezen from "../controllers/freezen";
import luckysheetsizeauto from '../controllers/resize';
import sheetmanage from '../controllers/sheetmanage';
import conditionformat from '../controllers/conditionformat';
import { luckysheet_searcharray } from "../controllers/sheetSearch";
import { selectHightlightShow, selectIsOverlap } from '../controllers/select';
import { sheetHTML, luckysheetdefaultstyle, rightclickHTML } from '../controllers/constant';
import { createFilterOptions, extendDataFilter, filterByCondition } from '../controllers/filter';
import controlHistory from '../controllers/controlHistory';
import { zoomRefreshView, zoomNumberDomBind } from '../controllers/zoom';
import dataVerificationCtrl from "../controllers/dataVerificationCtrl";
import imageCtrl from '../controllers/imageCtrl';
import dayjs from "dayjs";
import { luckysheetupdateCell } from '../controllers/updateCell';
import { deepCopy } from "../utils/chartUtil";
import { selectTextDom } from "./cursorPos";
import { colLocation, mouseposition, rowLocation } from "./location";
import { luckysheet } from "../core";
import insertFormula from "../controllers/insertFormula";
import { exportExcelFromJson } from "./toExcel";
import { hex2OrderMap } from "./stylesMap";
import { cloneDeep, isNil, isNumber, isString } from "lodash";
import rhchInit, { zoomSetting } from "./rhchInit";
import { inlineStyleAffectAttribute, updateInlineStringFormatOutside } from "../controllers/inlineString";
const jsondiffpatch = require('jsondiffpatch');
const IDCardReg = /^\d{6}(18|19|20)?\d{2}(0[1-9]|1[12])(0[1-9]|[12]\d|3[01])\d{3}(\d|X)$/i;

/**
 * 获取单元格的值
 * @param {Number} row 单元格所在行数；从0开始的整数，0表示第一行
 * @param {Number} column 单元格所在列数；从0开始的整数，0表示第一列
 * @param {Object} options 可选参数
 * @param {String} options.type 单元格的值类型，可以设置为原始值"v"或者显示值"m"；默认值为'v',表示获取单元格的实际值
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 */
export function getCellValue (row, column, options = {}) {
    if (!isRealNum(row) || !isRealNum(column)) {
        return;
    }
    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        type = 'v',
        order = curSheetOrder
    } = { ...options };
    let targetSheetData = Store.luckysheetfile[order].data;
    let sheetIndex = method.getIndexByOrder(order);
    let cellData;
    if (row >= targetSheetData.length) {
        return;
    } else {
        cellData = targetSheetData[row][column];
    }
    let return_v;
    if (getObjType(cellData) == "object") {
        return_v = cellData[type];

        if (type == "f" && return_v != null) {
            return_v = formula.functionHTMLGenerate(return_v);
        } else if (type == "f") {
            return_v = cellData.v;
        } else if (cellData && cellData.ct && cellData.ct.fa == 'yyyy-MM-dd') {
            return_v = cellData.m;
        }
    }

    if (return_v == undefined) {
        return_v = null;
    }

    return return_v;
}

/**
 * 获取单元格的信息
 * @param {Number} row 单元格所在行数；从0开始的整数，0表示第一行
 * @param {Number} column 单元格所在列数；从0开始的整数，0表示第一列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 */
export function getCellInfo (row, column, options = {}) {
    if (!isRealNum(row) || !isRealNum(column)) {
        return;
    }
    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        order = curSheetOrder,
        execFunc = false,
        validate = false
    } = { ...options };
    let sheetIndex = method.getIndexByOrder(order);
    let file = Store.luckysheetfile[order];
    if (Store.luckysheetfile[order].data.length == 0) {
        file.data = sheetmanage.buildGridData(Store.luckysheetfile[order]);
    }
    let cellData;
    if (file.data[0]) {
        if (row >= file.data.length || column >= file.data[0].length) return null;
    } else {
        return null;
    }

    cellData = file.data[row][column];
    if (execFunc && cellData && cellData.f) {
        let v = formula.execfunction(cellData.f, row, column, file.index);
        cellData.v = v[1];
    }
    let isValidate;
    if (validate && cellData && file.dataVerification && file.dataVerification[`${row}_${column}`]) {
        let validateValue = cellData.v || '';
        if (cellData.ct && cellData.ct.t == 'd') {
            validateValue = cellData.m || '';
        }
        isValidate = dataVerificationCtrl.validateCellData(validateValue.toString(), file.dataVerification[`${row}_${column}`], file.index);
    }
    let cf = {};
    if (validate && file.computeMap && file.computeMap[`${row}_${column}`]) {
        cf = file.computeMap[`${row}_${column}`];
    }
    let m;
    if (cellData) {
        m = cellData.m;
        if (cellData.ct && cellData.ct.t === 'inlineStr') {
            m = cellData.v;
        }
    }

    let res = {
        ...cellData,
        ...cf,
        m
    };
    if (validate) {
        res.isValidate = isValidate;
    }

    return res;
}

// 检索行列对应单元格数据
function getCellFromCellData (r, c, celldata, rowCount, columnCount) {
    let index = r * columnCount + c;
    let tCell = celldata[index];
    if (tCell.r == r && tCell.c == c) return tCell;
    return celldata.find((cItem) => {
        return cItem && cItem.r == r && cItem.c == c;
    });
}

/**
 * 设置单元格的值
 *
 * 关键点：如果设置了公式，则需要更新公式链insertUpdateFunctionGroup，如果设置了不是公式，判断之前是公式，则需要清除公式delFunctionGroup
 *
 * @param {Number} row 单元格所在行数；从0开始的整数，0表示第一行
 * @param {Number} column 单元格所在列数；从0开始的整数，0表示第一列
 * @param {Object | String | Number} value 要设置的值；可以为字符串或数字，或为符合Luckysheet单元格格式的对象
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Boolean} options.isRefresh 是否刷新界面；默认为`true`
 * @param {Function} options.success 操作结束的回调函数
 */
export function setCellValue (row, column, value, options = {}) {
    // if (!isRealNum(row) || !isRealNum(column)) {
    //     return tooltip.info('The row or column parameter is invalid.', '');
    // }
    let {
        order = getSheetIndex(Store.currentSheetIndex),
        isFormat = false,
        triggerHook = true,
        success
    } = options;
    let curv;
    let isRefresh = !Store.suspend;
    // Store old value for hook function
    let oldValue;
    if (triggerHook) {
        try {
            curv = getCellInfo(row, column, { order }) || {};
        } catch {
            curv = {};
        }

        oldValue = JSON.stringify(curv);
    }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        console.error('根据传入的 order 找不到对应的 sheet');
        return;
    }

    /* cell更新前触发  */
    if (!method.createHookFunction("cellUpdateBefore", row, column, value, isRefresh)) {
        /* 如果cellUpdateBefore函数返回false 则不执行后续的更新 */
        return;
    }

    let data = file.data;
    if (!data.length > 0) {
        file.data = buildGridData(file);
        data = file.data;
    }
    if (row < file.data.length) {
        setCellValueLoaded(row, column, value, order, isRefresh, file, isFormat);
    }
    /* 如何 triggerHook 为 true cell更新后触发  */
    if (triggerHook) {
        setTimeout(() => {
            // Hook function
            oldValue = JSON.parse(oldValue);
            if (oldValue && oldValue.ct &&  oldValue.ct.t === 'inlineStr') {
                oldValue.m = oldValue.v;
            }
            let cc = getCellInfo(row, column, { order });
            let updateValue = cc
                ? {
                    ...cc
                }
                : null;
            if (updateValue && updateValue.ct && updateValue.ct.t === 'inlineStr') {
                updateValue.m = updateValue.v;
            }
            method.createHookFunction("cellUpdated", row, column, oldValue, updateValue, order);
        }, 0);
    }
    if (success && typeof success === 'function') {
        success(data, JSON.parse(oldValue));
    }
}

// 对已经加载出来数据设置值
function setCellValueLoaded (row, column, value, order, isRefresh, file, isFormat) {
    let data = file.data;
    if (data.length == 0) {
        file.data = sheetmanage.buildGridData(file);
        data = file.data;
    }

    // luckysheetformula.updatecell(row, column, value);
    let formatList = {
        // ct:1, //celltype,Cell value format: text, time, etc.
        bg: 1, // background,#fff000
        ff: 1, // fontfamily,
        fc: 1, // fontcolor
        bl: 1, // Bold
        it: 1, // italic
        fs: 1, // font size
        cl: 1, // Cancelline, 0 Regular, 1 Cancelline
        un: 1, // underline, 0 Regular, 1 underlines, fonts
        vt: 1, // Vertical alignment, 0 middle, 1 up, 2 down
        ht: 1, // Horizontal alignment,0 center, 1 left, 2 right
        mc: 1, // Merge Cells
        tr: 1, // Text rotation,0: 0、1: 45 、2: -45、3 Vertical text、4: 90 、5: -90
        tb: 1, // Text wrap,0 truncation, 1 overflow, 2 word wrap
        // v: 1, //Original value
        // m: 1, //Display value
        rt: 1, // text rotation angle 0-180 alignment
        // f: 1, //formula
        qp: 1 // quotePrefix, show number as string
    };

    if (value == null || value.toString().length == 0) {
        formula.delFunctionGroup(row, column);
        initsetcellValue(row, column, data, value);
        if (Store.syncOrigin.flag) {
            data[row][column].ov = data[row][column].v;
            if (data[row][column].ct && data[row][column].ct.s && data[row][column].ct.s.length) {
                let concatV = data[row][column].ct.s.map((s) => s.v).join('');
                data[row][column].ov = concatV;
            }
            data[row][column].of = data[row][column].f;
        }
    } else if (value instanceof Object) {
        let curv = {};
        if (isRealNull(data[row][column])) {
            data[row][column] = {};
        }
        let cell = data[row][column];
        if (value.f != null && value.v == null) {
            curv.f = value.f;
            if (value.ct != null) {
                curv.ct = value.ct;
            }
            if (file.index === Store.currentSheetIndex) {
                data = formula.updatecell(row, column, curv, false, false, false, false, false).data;// update formula value
            } else {
                let v = formula.execfunction(value.f, row, column, file.index);
                data[row][column].v = v[1];
            }
        } else {
            if (value.ct != null) {
                curv.ct = value.ct;
            }
            if (value.f != null) {
                curv.f = value.f;
            }
            if (value.v != null) {
                curv.v = value.v;
            } else if (value.v === undefined) {
                curv.v = cell.v;
            } else {
                curv.v = null;
            }
            if (value.m != null) {
                curv.m = value.m;
            }
            formula.delFunctionGroup(row, column, file.index);
            initsetcellValue(row, column, data, curv);// update text value
        }
        for (let attr in value) {
            let v = value[attr];
            if (attr in formatList) {
                menuButton.updateFormatCell(data, attr, v, row, row, column, column);// change range format
            } else {
                cell[attr] = v;
            }
        }
        data[row][column] = cell;
        if (Store.syncOrigin.flag) {
            data[row][column].ov = data[row][column].v;
            if (data[row][column].ct && data[row][column].ct.s && data[row][column].ct.s.length) {
                let concatV = data[row][column].ct.s.map((s) => s.v).join('');
                data[row][column].ov = concatV;
            }
            data[row][column].of = data[row][column].f;
        }
    } else {
        if (value.toString().substr(0, 1) == "=" || value.toString().substr(0, 5) == "<span") {
            if (file.index === Store.currentSheetIndex) {
                data = formula.updatecell(row, column, value, false).data;// update formula value or convert inline string html to object
            }
        } else {
            formula.delFunctionGroup(row, column, file.index);
            setcellvalue(row, column, data, value);
        }
        if (Store.syncOrigin.flag) {
            data[row][column].ov = data[row][column].v;
            if (data[row][column].ct && data[row][column].ct.s && data[row][column].ct.s.length) {
                let concatV = data[row][column].ct.s.map((s) => s.v).join('');
                data[row][column].ov = concatV;
            }
            data[row][column].of = data[row][column].f;
        }
    }
    if (file.index == Store.currentSheetIndex) {
        Store.luckysheetfile[getCurrentSheetIndex()].computeMap = null;
        if (!Store.suspend) {
            jfrefreshgrid(data, [{ "row": [row, row], "column": [column, column] }]);// update data, meanwhile refresh canvas and store data to history
            formula.cancelNormalSelected();
        } else {
            Store.flowdata = data;
            file.data = Store.flowdata;
            formula.execFunctionExist = formula.execFunctionExist || [];
            formula.execFunctionExist.push({ "r": row, "c": column, "i": file.index });
        }
    } else {
        file.data = data;// only update data
        formula.execFunctionExist = formula.execFunctionExist || [];
        formula.execFunctionExist.push({ "r": row, "c": column, "i": file.index });
    }
}

/**
 * 清除指定工作表指定单元格的内容，返回清除掉的数据，不同于删除单元格的功能，不需要设定单元格移动情况
 * @param {Number} row 单元格所在行数；从0开始的整数，0表示第一行
 * @param {Number} column 单元格所在列数；从0开始的整数，0表示第一列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function clearCell (row, column, options = {}) {
    if (!isRealNum(row) || !isRealNum(column)) {
        return;
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        order = curSheetOrder,
        success
    } = { ...options };

    let targetSheetData = $.extend(true, [], Store.luckysheetfile[order].data);
    let cell = targetSheetData[row][column];

    if (getObjType(cell) == "object") {
        delete cell.m;
        delete cell.v;

        if (cell.f != null) {
            delete cell.f;
            formula.delFunctionGroup(row, column, order);

            delete cell.spl;
        }
    } else {
        cell = null;
    }

    // 若操作为当前sheet页，则刷新当前sheet页
    if (order === curSheetOrder) {
        jfrefreshgrid(targetSheetData, [{
            row: [row, row],
            column: [column, column]
        }]);
    } else {
        Store.luckysheetfile[order].data = targetSheetData;
    }

    if (success && typeof success === 'function') {
        success(cell);
    }
}

/**
 * 删除指定工作表指定单元格，返回删除掉的数据，同时，指定是右侧单元格左移还是下方单元格上移
 * @param {String} move 删除后，右侧还是下方的单元格移动。可选值为 'left'、'up'
 * @param {Number} row 单元格所在行数；从0开始的整数，0表示第一行
 * @param {Number} column 单元格所在列数；从0开始的整数，0表示第一列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteCell (move, row, column, options = {}) {
    let moveTypes = ['left', 'up'];
    if (!move || moveTypes.indexOf(move) < 0) {
        return;
    }

    if (!isRealNum(row) || !isRealNum(column)) {
        return;
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        order = curSheetOrder,
        success
    } = { ...options };

    let moveType = 'move' + move.replace(move[0], move[0].toUpperCase()); // left-moveLeft;  up-moveUp

    let sheetIndex;
    if (order) {
        if (Store.luckysheetfile[order]) {
            sheetIndex = Store.luckysheetfile[order].index;
        }
    }

    luckysheetDeleteCell(moveType, row, row, column, column, sheetIndex);

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 设置某个单元格的属性，如果要设置单元格的值或者同时设置多个单元格属性，推荐使用setCellValue
 * @param {Number} row 单元格所在行数；从0开始的整数，0表示第一行
 * @param {Number} column 单元格所在列数；从0开始的整数，0表示第一列
 * @param {String} attr
 * @param {Number | String | Object} value 具体的设置值，一个属性会对应多个值，参考 单元格属性表的值示例，特殊情况：如果属性类型attr是单元格格式ct，则设置值value应提供ct.fa，比如设置A1单元格的格式为百分比格式：luckysheet.setCellFormat(0, 0, "ct", "0.00%")
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数, callback参数为改变后的cell对象
 */
export function setCellFormat (row, column, attr, value, options = {}) {
    if (!isRealNum(row) || !isRealNum(column)) {
        return tooltip.info('Arguments row or column cannot be null or undefined.', '');
    }

    if (!attr) {
        return tooltip.info('Arguments attr cannot be null or undefined.', '');
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        order = curSheetOrder,
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let targetSheetData = file.data;
    if (targetSheetData.length == 0) {
        targetSheetData = sheetmanage.buildGridData(file);
    }
    if (targetSheetData.length >= row) {
        let cellData = targetSheetData[row][column] || {};
        let cfg = file.config;

        // 特殊格式
        if (attr == 'ct' && (!value || !value.hasOwnProperty('fa') || !value.hasOwnProperty('t'))) {
            return new TypeError('While set attribute \'ct\' to cell, the value must have property \'fa\' and \'t\'');
        }

        if (attr == 'bd') {
            if (cfg.borderInfo == null) {
                cfg.borderInfo = [];
            }

            let borderInfo = {
                rangeType: "range",
                borderType: "border-all",
                color: "#000",
                style: "1",
                range: [{
                    column: [column, column],
                    row: [row, row]
                }],
                ...value
            };

            cfg.borderInfo.push(borderInfo);
        } else {
            cellData[attr] = value;
        }

        targetSheetData[row][column] = cellData;

        // refresh
        if (file.index == Store.currentSheetIndex) {
            file.config = cfg;
            Store.config = cfg;
            jfrefreshgrid(targetSheetData, [{ "row": [row, row], "column": [column, column] }]);
        } else {
            file.config = cfg;
            file.data = targetSheetData;
        }
    } else {
        const sheet = sheetmanage.getSheetByIndex(file.index);
        if (sheet) {
            const cell = sheet.celldata.find((cItem) => {
                return cItem && cItem.r == row && cItem.c == column;
            });
            if (cell && cell.v) cell.v[attr] = value;
        }
    }

    if (success && typeof success === 'function') {
        success(cellData);
    }
}

/**
 * 查找一个工作表中的指定内容，返回查找到的内容组成的单元格一位数组，数据格式同celldata
 * @param {String} content 要查找的内容 可以为正则表达式（不包含前后'/')
 * @param {Object} options 可选参数
 * @param {Boolean} options.isRegularExpression 是否正则表达式匹配；默认为 false. 注意：正则中的规则需要转义，如\S需要写成 \\S
 * @param {Boolean} options.isWholeWord 是否整词匹配；默认为 false
 * @param {Boolean} options.isCaseSensitive 是否区分大小写匹配；默认为 false
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {String} options.type 单元格属性；默认值为m
 */
export function find (content, options = {}) {
    if (!content && content != 0) {
        return tooltip.info('Search content cannot be null or empty', '');
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        isRegularExpression = false,
        isWholeWord = false,
        isCaseSensitive = false,
        order = curSheetOrder,
        type = "m"
    } = { ...options };
    let targetSheetData = Store.luckysheetfile[order].data;

    let result = [];
    for (let i = 0; i < targetSheetData.length; i++) {
        const rowArr = targetSheetData[i];

        for (let j = 0; j < rowArr.length; j++) {
            const cell = rowArr[j];

            if (!cell) {
                continue;
            }

            // 添加cell的row, column属性
            // replace方法中的setCellValue中需要使用该属性
            cell.row = i;
            cell.column = j;

            if (isWholeWord) {
                if (isCaseSensitive) {
                    if (content.toString() == cell[type]) {
                        result.push(cell);
                    }
                } else {
                    if (cell[type] && content.toString().toLowerCase() == cell[type].toLowerCase()) {
                        result.push(cell);
                    }
                }
            } else if (isRegularExpression) {
                let reg;
                if (isCaseSensitive) {
                    reg = new RegExp(func_methods.getRegExpStr(content), 'g');
                } else {
                    reg = new RegExp(func_methods.getRegExpStr(content), 'ig');
                }
                if (reg.test(cell[type])) {
                    result.push(cell);
                }
            } else if (isCaseSensitive) {
                let reg = new RegExp(func_methods.getRegExpStr(content), 'g');
                if (reg.test(cell[type])) {
                    result.push(cell);
                }
            } else {
                let reg = new RegExp(func_methods.getRegExpStr(content), 'ig');
                if (reg.test(cell[type])) {
                    result.push(cell);
                }
            }
        }
    }

    return result;
}

/**
 * 查找一个工作表中的指定内容并替换成新的内容，返回替换后的内容组成的单元格一位数组，数据格式同celldata。
 * @param {String} content 要查找的内容
 * @param {String} replaceContent 要替换的内容
 * @param {Object} options 可选参数
 * @param {Boolean} options.isRegularExpression 是否正则表达式匹配；默认为 false
 * @param {Boolean} options.isWholeWord 是否整词匹配；默认为 false
 * @param {Boolean} options.isCaseSensitive 是否区分大小写匹配；默认为 false
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数, callback参数为替换后的cell集合
 */
export function replace (content, replaceContent, options = {}) {
    let matchCells = find(content, options);
    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        order = curSheetOrder
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }
    let sheetData = $.extend(true, [], file.data);

    matchCells.forEach(cell => {
        cell.m = replaceContent;
        setCellValue(cell.row, cell.column, replaceContent, { order: order, isRefresh: false });
    });

    let fileData = $.extend(true, [], file.data);
    file.data.length = 0;
    file.data.push(...sheetData);

    if (file.index == Store.currentSheetIndex) {
        jfrefreshgrid(fileData, undefined, undefined, true, false);
    }

    luckysheetrefreshgrid();

    if (options.success && typeof options.success === 'function') {
        options.success(matchCells);
    }
    return matchCells;
}

/**
 * 手动触发退出编辑模式
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function exitEditMode (options = {}) {
    if (parseInt($("#luckysheet-input-box").css("top")) > 0) {
        if ($("#luckysheet-formula-search-c").is(":visible") && formula.searchFunctionCell != null) {
            formula.searchFunctionEnter($("#luckysheet-formula-search-c").find(".luckysheet-formula-search-item-active"));
        } else {
            formula.updatecell(Store.luckysheetCellUpdate[0], Store.luckysheetCellUpdate[1]);
            Store.luckysheet_select_save = [{
                "row": [Store.luckysheetCellUpdate[0], Store.luckysheetCellUpdate[0]],
                "column": [Store.luckysheetCellUpdate[1], Store.luckysheetCellUpdate[1]],
                "row_focus": Store.luckysheetCellUpdate[0],
                "column_focus": Store.luckysheetCellUpdate[1]
            }];
        }

        // 若有参数弹出框，隐藏
        if ($("#luckysheet-search-formula-parm").is(":visible")) {
            $("#luckysheet-search-formula-parm").hide();
        }
        // 若有参数选取范围弹出框，隐藏
        if ($("#luckysheet-search-formula-parm-select").is(":visible")) {
            $("#luckysheet-search-formula-parm-select").hide();
        }
    }

    if (options.success && typeof options.success === 'function') {
        options.success();
    }
}

/**
 * 手动触发进入编辑模式
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function enterEditMode (options = {}) {
    if ($("#luckysheet-conditionformat-dialog").is(":visible")) {
        return;
    } else if ($("#luckysheet-cell-selected").is(":visible")) {
        let last = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1];

        let row_index = last.row_focus, col_index = last.column_focus;

        luckysheetupdateCell(row_index, col_index, Store.flowdata);
    }

    if (options.success && typeof options.success === 'function') {
        options.success();
    }
}

/**
 * 冻结首行
 * 若设置冻结的sheet不是当前sheet页，只设置参数不渲染
 * @param {Number | String} order 工作表索引
 */
export function frozenFirstRow (order) {
    // store frozen
    luckysheetFreezen.saveFrozen("freezenRow", order);

    // 冻结为当前sheet页
    if (!order || order == getSheetIndex(Store.currentSheetIndex)) {
        let freezenhorizontaldata, row_st, top;
        if (luckysheetFreezen.freezenRealFirstRowColumn) {
            let row_st = 0;
            top = Store.visibledatarow[row_st] - 2 + Store.columnHeaderHeight;
            freezenhorizontaldata = [
                Store.visibledatarow[row_st],
                row_st + 1,
                0,
                luckysheetFreezen.cutVolumn(Store.visibledatarow, row_st + 1),
                top
            ];
        } else {
            let scrollTop = $("#luckysheet-cell-main").scrollTop();
            row_st = luckysheet_searcharray(Store.visibledatarow, scrollTop);
            if (row_st == -1) {
                row_st = 0;
            }

            top = Store.visibledatarow[row_st] - 2 - scrollTop + Store.columnHeaderHeight;
            freezenhorizontaldata = [
                Store.visibledatarow[row_st],
                row_st + 1,
                scrollTop,
                luckysheetFreezen.cutVolumn(Store.visibledatarow, row_st + 1),
                top
            ];
        }

        luckysheetFreezen.saveFreezen(freezenhorizontaldata, top, null, null);

        if (luckysheetFreezen.freezenverticaldata != null) {
            luckysheetFreezen.cancelFreezenVertical();
            luckysheetFreezen.createAssistCanvas();
            luckysheetrefreshgrid();
        }

        luckysheetFreezen.createFreezenHorizontal(freezenhorizontaldata, top);
        luckysheetFreezen.createAssistCanvas();
        luckysheetrefreshgrid();
    }
}

/**
 * 冻结首列
 * 若设置冻结的sheet不是当前sheet页，只设置参数不渲染
 * @param {Number | String} order 工作表索引
 */
export function frozenFirstColumn (order) {
    // store frozen
    luckysheetFreezen.saveFrozen("freezenColumn", order);

    // 冻结为当前sheet页
    if (!order || order == getSheetIndex(Store.currentSheetIndex)) {
        let freezenverticaldata, col_st, left;
        if (luckysheetFreezen.freezenRealFirstRowColumn) {
            col_st = 0;
            left = Store.visibledatacolumn[col_st] - 2 + Store.rowHeaderWidth;
            freezenverticaldata = [
                Store.visibledatacolumn[col_st],
                col_st + 1,
                0,
                luckysheetFreezen.cutVolumn(Store.visibledatacolumn, col_st + 1),
                left
            ];
        } else {
            let scrollLeft = $("#luckysheet-cell-main").scrollLeft();

            col_st = luckysheet_searcharray(Store.visibledatacolumn, scrollLeft);
            if (col_st == -1) {
                col_st = 0;
            }

            left = Store.visibledatacolumn[col_st] - 2 - scrollLeft + Store.rowHeaderWidth;
            freezenverticaldata = [
                Store.visibledatacolumn[col_st],
                col_st + 1,
                scrollLeft,
                luckysheetFreezen.cutVolumn(Store.visibledatacolumn, col_st + 1),
                left
            ];
        }

        luckysheetFreezen.saveFreezen(null, null, freezenverticaldata, left);

        if (luckysheetFreezen.freezenhorizontaldata != null) {
            luckysheetFreezen.cancelFreezenHorizontal();
            luckysheetFreezen.createAssistCanvas();
            luckysheetrefreshgrid();
        }

        luckysheetFreezen.createFreezenVertical(freezenverticaldata, left);
        luckysheetFreezen.createAssistCanvas();
        luckysheetrefreshgrid();
    }
}

/**
 * 冻结行选区
 * @param {Object} range 行选区范围的focus单元格的行列值构成的对象；格式为{ row_focus:0, column_focus:0 }
 * @param {Number | String} order 工作表索引
 */
export function frozenRowRange (range, order) {
    const locale_frozen = locale().freezen;

    if (!range || (!range.hasOwnProperty('row_focus') && !formula.iscelldata(range))) {
        if (isEditMode()) {
            alert(locale_frozen.noSeletionError);
        } else {
            tooltip.info(locale_frozen.noSeletionError, "");
        }
        return;
    }

    if (typeof range === 'string' && formula.iscelldata(range)) {
        range = formula.getcellrange(range);
        range = {
            row_focus: range.row[0],
            column_focus: range.column[0]
        };
    }
    // store frozen
    luckysheetFreezen.saveFrozen("freezenRowRange", order, range);

    if (!order || order == getSheetIndex(Store.currentSheetIndex)) {
        let scrollTop = $("#luckysheet-cell-main").scrollTop();
        let row_st = luckysheet_searcharray(Store.visibledatarow, scrollTop);

        let row_focus = range.row_focus;
        if (row_focus > row_st) {
            row_st = row_focus;
        }
        if (row_st == -1) {
            row_st = 0;
        }

        let top = Store.visibledatarow[row_st] - 2 - scrollTop + Store.columnHeaderHeight;
        let freezenhorizontaldata = [
            Store.visibledatarow[row_st],
            row_st + 1,
            scrollTop,
            luckysheetFreezen.cutVolumn(Store.visibledatarow, row_st + 1),
            top
        ];
        luckysheetFreezen.saveFreezen(freezenhorizontaldata, top, null, null);

        if (luckysheetFreezen.freezenverticaldata != null) {
            luckysheetFreezen.cancelFreezenVertical();
            luckysheetFreezen.createAssistCanvas();
            luckysheetrefreshgrid();
        }

        luckysheetFreezen.createFreezenHorizontal(freezenhorizontaldata, top);
        luckysheetFreezen.createAssistCanvas();
        luckysheetrefreshgrid();
    }
}

/**
 * 冻结列选区
 * @param {Object} range 列选区范围的focus单元格的行列值构成的对象；格式为{ row_focus:0, column_focus:0 }
 * @param {Number | String} order 工作表索引
 */
export function frozenColumnRange (range, order) {
    const locale_frozen = locale().freezen;
    let isStringRange = typeof range === 'string' && formula.iscelldata(range);

    if (!range || (!range.hasOwnProperty('column_focus') && !isStringRange)) {
        if (isEditMode()) {
            alert(locale_frozen.noSeletionError);
        } else {
            tooltip.info(locale_frozen.noSeletionError, "");
        }
        return;
    }

    if (isStringRange) {
        range = formula.getcellrange(range);
        range = {
            row_focus: range.row[0],
            column_focus: range.column[0]
        };
    }
    // store frozen
    luckysheetFreezen.saveFrozen("freezenColumnRange", order, range);

    if (!order || order == getSheetIndex(Store.currentSheetIndex)) {
        let scrollLeft = $("#luckysheet-cell-main").scrollLeft();
        let col_st = luckysheet_searcharray(Store.visibledatacolumn, scrollLeft);

        let column_focus = range.column_focus;
        if (column_focus > col_st) {
            col_st = column_focus;
        }
        if (col_st == -1) {
            col_st = 0;
        }

        let left = Store.visibledatacolumn[col_st] - 2 - scrollLeft + Store.rowHeaderWidth;
        let freezenverticaldata = [
            Store.visibledatacolumn[col_st],
            col_st + 1,
            scrollLeft,
            luckysheetFreezen.cutVolumn(Store.visibledatacolumn, col_st + 1),
            left
        ];
        luckysheetFreezen.saveFreezen(null, null, freezenverticaldata, left);

        if (luckysheetFreezen.freezenhorizontaldata != null) {
            luckysheetFreezen.cancelFreezenHorizontal();
            luckysheetFreezen.createAssistCanvas();
            luckysheetrefreshgrid();
        }

        luckysheetFreezen.createFreezenVertical(freezenverticaldata, left);
        luckysheetFreezen.createAssistCanvas();
        luckysheetrefreshgrid();
    }
}

/**
 * 取消冻结
 * @param {Number | String} order
 */
export function cancelFrozen (order) {
    luckysheetFreezen.saveFrozen("freezenCancel", order);

    // 取消当前sheet冻结时，刷新canvas
    if (!order || order == getSheetIndex(Store.currentSheetIndex)) {
        if (luckysheetFreezen.freezenverticaldata != null) {
            luckysheetFreezen.cancelFreezenVertical();
        }
        if (luckysheetFreezen.freezenhorizontaldata != null) {
            luckysheetFreezen.cancelFreezenHorizontal();
        }
        luckysheetFreezen.createAssistCanvas();
        luckysheetrefreshgrid();
    }
}

/**
 * 冻结行操作。特别注意，只有在isRange设置为true的时候，才需要设置setting中的range，且与一般的range格式不同。
 * @param {Boolean} isRange 是否冻结行到选区 true-冻结行到选区  false-冻结首行
 * @param {Object} options 可选参数
 * @param {Object} options.range isRange为true的时候设置，开启冻结的单元格位置，格式为{ row_focus:0, column_focus:0 }，意为当前激活的单元格的行数和列数；默认从当前选区最后的一个选区中取得
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setHorizontalFrozen (isRange, options = {}) {
    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        range,
        order = curSheetOrder,
        success
    } = { ...options };

    // 若已存在冻结，取消之前的冻结效果
    cancelFrozen(order);

    if (!isRange) {
        frozenFirstRow(order);
    } else { // 选区行冻结
        frozenRowRange(range, order);
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 冻结列操作。特别注意，只有在isRange设置为true的时候，才需要设置setting中的range，且与一般的range格式不同。
 * @param {Boolean} isRange 是否冻结列到选区 true-冻结列到选区  false-冻结首列
 * @param {Object} options 可选参数
 * @param {Object} options.range isRange为true的时候设置，开启冻结的单元格位置，格式为{ row_focus:0, column_focus:0 }，意为当前激活的单元格的行数和列数；默认从当前选区最后的一个选区中取得
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setVerticalFrozen (isRange, options = {}) {
    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        range,
        order = curSheetOrder,
        success
    } = { ...options };

    // 若已存在冻结，取消之前的冻结效果
    cancelFrozen(order);

    if (!isRange) {
        frozenFirstColumn(order);
    } else {
        frozenColumnRange(range, order);
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 冻结行列操作。特别注意，只有在isRange设置为true的时候，才需要设置setting中的range，且与一般的range格式不同。
 * @param {Boolean} isRange 是否冻结行列到选区 true-冻结行列到选区  false-冻结首行列
 * @param {Object} options 可选参数
 * @param {Object} options.range isRange为true的时候设置，开启冻结的单元格位置，格式为{ row_focus:0, column_focus:0 }，意为当前激活的单元格的行数和列数；默认从当前选区最后的一个选区中取得
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setBothFrozen (isRange, options = {}) {
    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        range,
        order = curSheetOrder,
        success
    } = { ...options };

    let isCurrentSheet = !order || order == getSheetIndex(Store.currentSheetIndex);
    const locale_frozen = locale().freezen;

    // 若已存在冻结，取消之前的冻结效果
    cancelFrozen(order);

    // 冻结首行列
    if (!isRange) {
        // store frozen
        luckysheetFreezen.saveFrozen("freezenRC", order);

        if (isCurrentSheet) {
            let scrollTop = $("#luckysheet-cell-main").scrollTop();
            let row_st = luckysheet_searcharray(Store.visibledatarow, scrollTop);
            if (row_st == -1) {
                row_st = 0;
            }
            let top = Store.visibledatarow[row_st] - 2 - scrollTop + Store.columnHeaderHeight;
            let freezenhorizontaldata = [
                Store.visibledatarow[row_st],
                row_st + 1,
                scrollTop,
                luckysheetFreezen.cutVolumn(Store.visibledatarow, row_st + 1),
                top
            ];
            luckysheetFreezen.saveFreezen(freezenhorizontaldata, top, null, null);

            luckysheetFreezen.createFreezenHorizontal(freezenhorizontaldata, top);

            let scrollLeft = $("#luckysheet-cell-main").scrollLeft();
            let col_st = luckysheet_searcharray(Store.visibledatacolumn, scrollLeft);
            if (col_st == -1) {
                col_st = 0;
            }
            let left = Store.visibledatacolumn[col_st] - 2 - scrollLeft + Store.rowHeaderWidth;
            let freezenverticaldata = [
                Store.visibledatacolumn[col_st],
                col_st + 1,
                scrollLeft,
                luckysheetFreezen.cutVolumn(Store.visibledatacolumn, col_st + 1),
                left
            ];
            luckysheetFreezen.saveFreezen(null, null, freezenverticaldata, left);

            luckysheetFreezen.createFreezenVertical(freezenverticaldata, left);

            luckysheetFreezen.createAssistCanvas();
            luckysheetrefreshgrid();
        }
    } else {   // 冻结行列到选区
        // store frozen
        luckysheetFreezen.saveFrozen("freezenRCRange", order, range);

        let isStringRange = typeof range === 'string' && formula.iscelldata(range);
        if (isCurrentSheet) {
            if ((!range || !(range.hasOwnProperty('column_focus') && range.hasOwnProperty('row_focus'))) && !isStringRange) {
                if (isEditMode()) {
                    alert(locale_frozen.noSeletionError);
                } else {
                    tooltip.info(locale_frozen.noSeletionError, "");
                }
                return;
            }

            if (isStringRange) {
                range = formula.getcellrange(range);
                range = {
                    row_focus: range.row[0],
                    column_focus: range.column[0]
                };
            }

            let scrollTop = $("#luckysheet-cell-main").scrollTop();
            let row_st = luckysheet_searcharray(Store.visibledatarow, scrollTop);

            let row_focus = range.row_focus;

            if (row_focus > row_st) {
                row_st = row_focus;
            }

            if (row_st == -1) {
                row_st = 0;
            }

            let top = Store.visibledatarow[row_st] - 2 - scrollTop + Store.columnHeaderHeight;
            let freezenhorizontaldata = [
                Store.visibledatarow[row_st],
                row_st + 1,
                scrollTop,
                luckysheetFreezen.cutVolumn(Store.visibledatarow, row_st + 1),
                top
            ];
            luckysheetFreezen.saveFreezen(freezenhorizontaldata, top, null, null);

            luckysheetFreezen.createFreezenHorizontal(freezenhorizontaldata, top);

            let scrollLeft = $("#luckysheet-cell-main").scrollLeft();
            let col_st = luckysheet_searcharray(Store.visibledatacolumn, scrollLeft);

            let column_focus = range.column_focus;

            if (column_focus > col_st) {
                col_st = column_focus;
            }

            if (col_st == -1) {
                col_st = 0;
            }

            let left = Store.visibledatacolumn[col_st] - 2 - scrollLeft + Store.rowHeaderWidth;
            let freezenverticaldata = [
                Store.visibledatacolumn[col_st],
                col_st + 1,
                scrollLeft,
                luckysheetFreezen.cutVolumn(Store.visibledatacolumn, col_st + 1),
                left
            ];
            luckysheetFreezen.saveFreezen(null, null, freezenverticaldata, left);

            luckysheetFreezen.createFreezenVertical(freezenverticaldata, left);

            luckysheetFreezen.createAssistCanvas();
            luckysheetrefreshgrid();
        }
    }
}

/**
 * 在第index行或列的位置，插入number行或列
 * @param {String} type 插入行或列 row-行  column-列
 * @param {Number} index 在第几行插入空白行，从0开始
 * @param {Object} options 可选参数
 * @param {Number} options.number 插入的空白行数；默认为 1
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function insertRowOrColumn (type, index = 0, options = {}) {
    if (!isRealNum(index)) {
        return tooltip.info('The index parameter is invalid.', '');
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        number = 1,
        order = curSheetOrder,
        success,
        scroll = true
    } = { ...options };

    let _locale = locale();
    let locale_info = _locale.info;
    if (!isRealNum(number)) {
        if (isEditMode()) {
            alert(locale_info.tipInputNumber);
        } else {
            tooltip.info(locale_info.tipInputNumber, "");
        }
        return;
    }

    number = parseInt(number);
    if (number < 1) {
        return;
    }

    // 默认在行上方增加行，列左侧增加列
    let sheetIndex;
    if (order) {
        if (Store.luckysheetfile[order]) {
            sheetIndex = Store.luckysheetfile[order].index;
        }
    }

    luckysheetextendtable(type, index, number, "lefttop", sheetIndex, scroll);

    if (success && typeof success === 'function') {
        success();
    }
}
/**
 * 在第index行或列的位置，插入number行或列
 * @param {String} type 插入行或列 row-行  column-列
 * @param {Number} index 在第几行插入空白行，从0开始
 * @param {Object} options 可选参数
 * @param {Number} options.number 插入的空白行数；默认为 1
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function insertRowBottomOrColumnRight (type, index = 0, options = {}) {
    if (!isRealNum(index)) {
        return tooltip.info('The index parameter is invalid.', '');
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        number = 1,
        order = curSheetOrder,
        success
    } = { ...options };

    let _locale = locale();
    let locale_info = _locale.info;
    if (!isRealNum(number)) {
        if (isEditMode()) {
            alert(locale_info.tipInputNumber);
        } else {
            tooltip.info(locale_info.tipInputNumber, "");
        }
        return;
    }

    number = parseInt(number);
    if (number < 1) {
        return;
    }

    // 默认在行上方增加行，列左侧增加列
    let sheetIndex;
    if (order) {
        if (Store.luckysheetfile[order]) {
            sheetIndex = Store.luckysheetfile[order].index;
        }
    }

    luckysheetextendtable(type, index, number, "rightbottom", sheetIndex);

    if (success && typeof success === 'function') {
        success();
    }
}
/**
 * 在第row行的位置，插入number行空白行
 * @param {Number} row 在第几行插入空白行，从0开始
 * @param {Object} options 可选参数
 * @param {Number} options.number 插入的空白行数；默认为 1
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function insertRow (row = 0, options = {}) {
    insertRowOrColumn('row', row, options);
}
/**
 * 在第row行的位置，插入number行空白行
 * @param {Number} row 在第几行插入空白行，从0开始
 * @param {Object} options 可选参数
 * @param {Number} options.number 插入的空白行数；默认为 1
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function insertRowBottom (row = 0, options = {}) {
    insertRowBottomOrColumnRight('row', row, options);
}
/**
 * 在第column列的位置，插入number列空白列
 * @param {Number} column 在第几列插入空白列，从0开始
 * @param {Object} options 可选参数
 * @param {Number} options.number 插入的空白列数；默认为 1
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function insertColumn (column = 0, options = {}) {
    insertRowOrColumn('column', column, options);
}
/**
 * 在第column列的位置，插入number列空白列
 * @param {Number} column 在第几列插入空白列，从0开始
 * @param {Object} options 可选参数
 * @param {Number} options.number 插入的空白列数；默认为 1
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function insertColumnRight (column = 0, options = {}) {
    insertRowBottomOrColumnRight('column', column, options);
}
/**
 * 删除指定的行或列。删除行列之后，行列的序号并不会变化，下面的行（右侧的列）会补充到上（左）面，注意观察数据是否被正确删除即可。
 * @param {String} type 删除行或列 row-行  column-列
 * @param {Number} startIndex 要删除的起始行或列
 * @param {Number} endIndex 要删除的结束行或列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteRowOrColumn (type, startIndex, endIndex, options = {}) {
    if (!isRealNum(startIndex) || !isRealNum(endIndex)) {
        return tooltip.info('Please enter the index for deleting rows or columns correctly.', '');
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        order = curSheetOrder,
        success
    } = { ...options };

    let sheetIndex;
    if (order) {
        if (Store.luckysheetfile[order]) {
            sheetIndex = Store.luckysheetfile[order].index;
        }
    }
    luckysheetdeletetable(type, startIndex, endIndex, sheetIndex);

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 删除指定的行。
 * @param {Number} rowStart 要删除的起始行
 * @param {Number} rowEnd 要删除的结束行
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteRow (rowStart, rowEnd, options = {}) {
    deleteRowOrColumn('row', rowStart, rowEnd, options);
}

/**
 * 删除指定的列。
 * @param {Number} columnStart 要删除的起始列
 * @param {Number} columnEnd 要删除的结束列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteColumn (columnStart, columnEnd, options = {}) {
    deleteRowOrColumn('column', columnStart, columnEnd, options);
}

/**
 * 隐藏行或列
 * @param {String} type 隐藏行或列  row-隐藏行  column-隐藏列
 * @param {Number} startIndex 起始行或列
 * @param {Number} endIndex 结束行或列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export async function hideRowOrColumn (type, startIndex, endIndex, options = {}) {
    if (!isRealNum(startIndex) || !isRealNum(endIndex)) {
        return tooltip.info('Please enter the index for deleting rows or columns correctly.', '');
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        order = curSheetOrder,
        saveParam = true,
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];
    let cfgKey = type === 'row' ? 'rowhidden' : 'colhidden';
    let cfg = file.config;
    if (cfg[cfgKey] == null) {
        cfg[cfgKey] = {};
    }

    for (let i = startIndex; i <= endIndex; i++) {
        cfg[cfgKey][i] = 0;
    }

    // 保存撤销
    if (Store.clearjfundo && !Store.suspend) {
        let redo = {};
        redo.type = type === 'row' ? 'showHidRows' : 'showHidCols';
        redo.sheetIndex = file.index;
        redo.config = $.extend(true, {}, file.config);
        redo.curconfig = cfg;

        Store.jfundo.length  = 0;
        Store.jfredo.push(redo);
    }

    Store.luckysheetfile[order].config = cfg;

    if (saveParam) {
        server.saveParam("cg", file.index, cfg[cfgKey], { "k": cfgKey });
    }

    // 若操作sheet为当前sheet页，行高、列宽 刷新
    if (order == curSheetOrder) {
        // config
        Store.config = cfg;
        jfrefreshgrid_rhcw(Store.flowdata.length, Store.flowdata[0].length, true, false);
    }
    if (success && typeof success === 'function') {
        success();
    }
}

function debounce (fn, ms) {
    if (Store.debounceTimer) {
        clearTimeout(Store.debounceTimer);
    }
    Store.debounceTimer = setTimeout(() => {
        fn();
        // 清空计时器
        Store.debounceTimer = null;
    }, ms);
}

function rowFullPage (cellCountOnePage, rowLen, sheetIndex) {
    let showRow = 0;
    let data = Store.luckysheetfile[sheetIndex].data;
    for (let i = 0; i < data.length; i++) {
        if (Store.config.rowhidden && !(i in Store.config.rowhidden)) showRow += 1;
    }
    if (data.length === rowLen) return true;
    return showRow >= cellCountOnePage;
}

/**
 * 显示隐藏的行或列
 * @param {String} type 显示行或列  row-显示行  column-显示列
 * @param {Number} startIndex 起始行或列
 * @param {Number} endIndex 结束行或列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function showRowOrColumn (type, startIndex, endIndex, options = {}) {
    if (!isRealNum(startIndex) || !isRealNum(endIndex)) {
        return tooltip.info('Please enter the index for deleting rows or columns correctly.', '');
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        order = curSheetOrder,
        saveParam = true,
        rerender = true,
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];
    let cfgKey = type === 'row' ? 'rowhidden' : 'colhidden';
    let cfg = $.extend(true, {}, file.config);
    if (cfg[cfgKey] == null) {
        return;
    }
    for (let i = startIndex; i <= endIndex; i++) {
        delete cfg[cfgKey][i];
    }

    // 保存撤销
    if (Store.clearjfundo) {
        let redo = {};
        redo.type = type === 'row' ? 'showHidRows' : 'showHidCols';
        redo.sheetIndex = file.index;
        redo.config = $.extend(true, {}, file.config);
        redo.curconfig = cfg;

        Store.jfundo.length  = 0;
        Store.jfredo.push(redo);
    }

    // config
    Store.luckysheetfile[order].config = cfg;

    if (saveParam) {
        server.saveParam("cg", file.index, cfg[cfgKey], { "k": cfgKey });
    }

    // 若操作sheet为当前sheet页，行高、列宽 刷新
    if (order === curSheetOrder) {
        Store.config = cfg;
        jfrefreshgrid_rhcw(Store.flowdata.length, Store.flowdata[0].length, rerender, false);
    }

    if (success && typeof success === 'function') {
        success();
    }

    if (type === 'column') {
        setTimeout(() => {
            luckysheetFreezen.scrollAdaptOffilteroptions();
            jfrefreshgrid_rhcw(null, Store.flowdata[0].length);
            // luckysheetrefreshgrid();
        });
    }
}

/**
 * 隐藏指定的行或列
 * @param {String} type 隐藏行或列  row-隐藏行  column-隐藏列
 * @param {Array} indexList 隐藏行或列号
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export async function hideSpecifyRowOrColumn (type, indexList, options = {}) {
    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        order = curSheetOrder,
        saveParam = true,
        rerender = true,
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];
    let cfgKey = type === 'row' ? 'rowhidden' : 'colhidden';
    let cfg = $.extend(true, {}, file.config);
    if (cfg[cfgKey] == null) {
        cfg[cfgKey] = {};
    }

    indexList.forEach((i) => {
        cfg[cfgKey][i] = 0;
    });

    // 保存撤销
    if (Store.clearjfundo) {
        let redo = {};
        redo.type = type === 'row' ? 'showHidRows' : 'showHidCols';
        redo.sheetIndex = file.index;
        redo.config = $.extend(true, {}, file.config);
        redo.curconfig = cfg;

        Store.jfundo.length  = 0;
        Store.jfredo.push(redo);
    }
    Store.luckysheetfile[order].config = cfg;

    if (saveParam) {
        server.saveParam("cg", file.index, cfg[cfgKey], { "k": cfgKey });
    }
    // 若操作sheet为当前sheet页，行高、列宽 刷新
    if (order == curSheetOrder) {
        // config
        Store.config = cfg;
        jfrefreshgrid_rhcw(Store.flowdata.length, Store.flowdata[0].length, rerender, false);
    }
    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 显示隐藏指定的的行或列
 * @param {String} type 显示行或列  row-显示行  column-显示列
 * @param {Array} indexList 显示的行或列号
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function showSpecifyRowOrColumn (type, indexList, options = {}) {
    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        order = curSheetOrder,
        saveParam = true,
        rerender = true,
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];
    let cfgKey = type === 'row' ? 'rowhidden' : 'colhidden';
    let cfg = file.config;
    if (cfg[cfgKey] == null) {
        return;
    }
    indexList.forEach((i) => {
        delete cfg[cfgKey][i];
    });

    // 保存撤销
    if (Store.clearjfundo && !Store.suspend) {
        let redo = {};
        redo.type = type === 'row' ? 'showHidRows' : 'showHidCols';
        redo.sheetIndex = file.index;
        redo.config = $.extend(true, {}, file.config);
        redo.curconfig = cfg;

        Store.jfundo.length  = 0;
        Store.jfredo.push(redo);
    }

    // config
    Store.luckysheetfile[order].config = cfg;

    if (saveParam) {
        server.saveParam("cg", file.index, cfg[cfgKey], { "k": cfgKey });
    }

    // 若操作sheet为当前sheet页，行高、列宽 刷新
    if (order === curSheetOrder) {
        Store.config = cfg;
        jfrefreshgrid_rhcw(Store.flowdata.length, Store.flowdata[0].length, rerender, false);
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 隐藏行
 * @param {Number} startIndex 起始行
 * @param {Number} endIndex 结束行
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function hideRow (startIndex, endIndex, options = {}) {
    hideRowOrColumn('row', startIndex, endIndex, options);
}

/**
 * 显示行
 * @param {Number} startIndex 起始行
 * @param {Number} endIndex 结束行
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function showRow (startIndex, endIndex, options = {}) {
    showRowOrColumn('row', startIndex, endIndex, options);
}

/**
 * 显示指定行
 * @param {Array} rowList 显示的行或列号
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function showSpecifyRow (rowList, options = {}) {
    showSpecifyRowOrColumn('row', rowList, options);
}

/**
 * 隐藏指定行
 * @param {Array} rowList 隐藏的行号
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function hideSpecifyRow (rowList, options = {}) {
    hideSpecifyRowOrColumn('row', rowList, options);
}

/**
 * 隐藏指定列
 * @param {Array} rowList 隐藏的列号
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function hideSpecifyColumn (columnList, options = {}) {
    hideSpecifyRowOrColumn('column', columnList, options);
    setTimeout(() => {
        luckysheetFreezen.scrollAdaptOffilteroptions();
    });
}

/**
 * 显示列
 * @param {Number} startIndex 起始列
 * @param {Number} endIndex 结束列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function showSpecifyColumn (columnList, options = {}) {
    showSpecifyRowOrColumn('column', columnList, options);
    setTimeout(() => {
        luckysheetFreezen.scrollAdaptOffilteroptions();
    });
}

/**
 * 隐藏列
 * @param {Number} startIndex 起始列
 * @param {Number} endIndex 结束列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function hideColumn (startIndex, endIndex, options = {}) {
    hideRowOrColumn('column', startIndex, endIndex, options);
    setTimeout(() => {
        luckysheetFreezen.scrollAdaptOffilteroptions();
    });
}

/**
 * 显示列
 * @param {Number} startIndex 起始列
 * @param {Number} endIndex 结束列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function showColumn (startIndex, endIndex, options = {}) {
    showRowOrColumn('column', startIndex, endIndex, options);
    setTimeout(() => {
        luckysheetFreezen.scrollAdaptOffilteroptions();
    });
}
/**
 * 设置指定行的高度。优先级最高，高于默认行高和用户自定义行高。
 * @param {Object} rowInfo 行数和高度对应关系
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setRowHeight (rowInfo, options = {}) {
    if (getObjType(rowInfo) != 'object') {
        return tooltip.info("The rowInfo parameter is invalid.", "");
    }

    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let cfg = $.extend(true, {}, file.config);
    if (cfg.rowlen == null) {
        cfg.rowlen = {};
    }

    for (let r in rowInfo) {
        if (parseInt(r) >= 0) {
            let len = rowInfo[r];

            if (len === 'auto') {
                cfg.rowlen[parseInt(r)] = len;
            } else {
                if (Number(len) >= 0) {
                    cfg.rowlen[parseInt(r)] = Number(len);
                }
            }
        }
    }

    file.config = cfg;

    server.saveParam("cg", file.index, cfg.rowlen, { "k": "rowlen" });

    if (file.index == Store.currentSheetIndex) {
        Store.config = cfg;
        jfrefreshgrid_rhcw(Store.flowdata.length, Store.flowdata[0].length);
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 设置指定列的宽度
 * @param {Object} columnInfo 行数和高度对应关系
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setColumnWidth (columnInfo, options = {}) {
    if (getObjType(columnInfo) != 'object') {
        return tooltip.info("The columnInfo parameter is invalid.", "");
    }

    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let cfg = file.config;
    if (cfg.columnlen == null) {
        cfg.columnlen = {};
    }

    for (let c in columnInfo) {
        if (parseInt(c) >= 0) {
            let len = columnInfo[c];

            if (len === 'auto') {
                cfg.columnlen[parseInt(c)] = len;
            } else {
                if (Number(len) >= 0) {
                    cfg.columnlen[parseInt(c)] = Number(len);
                }
            }
        }
    }

    file.config = cfg;

    server.saveParam("cg", file.index, cfg.columnlen, { "k": "columnlen" });

    if (file.index == Store.currentSheetIndex) {
        Store.config = cfg;
        jfrefreshgrid_rhcw(Store.flowdata.length, Store.flowdata[0].length, true, true, false);
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 获取指定工作表指定行的高度，得到行号和高度对应关系的对象
 * @param {Array} rowInfo 行号下标组成的数组；行号下标从0开始；
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function getRowHeight (rowInfo, options = {}) {
    if (getObjType(rowInfo) != 'array' || rowInfo.length == 0) {
        return tooltip.info("The rowInfo parameter is invalid.", "");
    }

    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let cfg = $.extend(true, {}, file.config);
    let rowlen = cfg.rowlen || {};

    let rowlenObj = {};

    rowInfo.forEach((item) => {
        if (parseInt(item) >= 0) {
            let size = rowlen[parseInt(item)] || Store.defaultrowlen;
            rowlenObj[parseInt(item)] = size;
        }
    });

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    return rowlenObj;
}

/**
 * 获取指定工作表指定列的宽度，得到列号和宽度对应关系的对象
 * @param {Array} columnInfo 行号下标组成的数组；行号下标从0开始；
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function getColumnWidth (columnInfo, options = {}) {
    if (getObjType(columnInfo) != 'array' || columnInfo.length == 0) {
        return tooltip.info("The columnInfo parameter is invalid.", "");
    }

    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let cfg = $.extend(true, {}, file.config);
    let columnlen = cfg.columnlen || {};

    let columnlenObj = {};

    columnInfo.forEach((item) => {
        if (parseInt(item) >= 0) {
            let size = columnlen[parseInt(item)] || Store.defaultcollen;
            columnlenObj[parseInt(item)] = size;
        }
    });

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    return columnlenObj;
}

/**
 * 获取工作表的默认行高
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function getDefaultRowHeight (options = {}) {
    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    return Store.luckysheetfile[order].defaultRowHeight;
}

/**
 * 获取工作表的默认列宽
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function getDefaultColWidth (options = {}) {
    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    return Store.luckysheetfile[order].defaultColWidth;
}

/**
 * 返回当前选区对象的数组，可能存在多个选区。
 * 每个选区的格式为row/column信息组成的对象{row:[0,1],column:[0,1]}
 * @returns {Array}
 */
export function getRange () {
    let rangeArr = JSON.parse(JSON.stringify(Store.luckysheet_select_save));

    let result = [];

    for (let i = 0; i < rangeArr.length; i++) {
        let rangeItem = rangeArr[i];
        let temp = {
            row: rangeItem.row,
            column: rangeItem.column
        };
        result.push(temp);
    }

    return result;
}

/**
 * 返回表示指定区域内所有单元格位置的数组，区别getRange方法，该方法以cell单元格(而非某块连续的区域)为单位来组织选区的数据
 * @param   {Array}   range 可选参数，默认为当前选中区域
 * @returns {Array}   对象数组
 */
export function getRangeWithFlatten (range) {
    range = range ||  getRange();

    let result = [];

    range.forEach(ele => {
        // 这个data可能是个范围或者是单个cell
        let rs = ele.row;
        let cs = ele.column;
        for (let r = rs[0]; r <= rs[1]; r++) {
            for (let c = cs[0]; c <= cs[1]; c++) {
                // r c 当前的r和当前的c
                result.push({ r, c });
            }
        }
    });
    return result;
}

/**
 * 返回表示指定区域内所有单元格内容的对象数组
 * @param   {Array}   range 可选参数，默认为当前选中区域扁平化后的对象，结构形如[{r:0,c:0},{r:0,c:1}...]
 * @returns {Array}   对象数组
 */
export function getRangeValuesWithFlatte (range) {
    range = range || getRangeWithFlatten();

    let values = [];

    // 获取到的这个数据不是最新的数据
    range.forEach(item => {
        values.push(Store.flowdata[item.r][item.c]);
    });
    return values;
}

/**
 * 返回对应当前选区的坐标字符串数组，可能存在多个选区。
 * 每个选区可能是单个单元格(如 A1)或多个单元格组成的矩形区域(如 D9:E12)
 * @returns {Array}
 */
export function getRangeAxis () {
    let result = [];
    let rangeArr = JSON.parse(JSON.stringify(Store.luckysheet_select_save));
    let sheetIndex = Store.currentSheetIndex;

    rangeArr.forEach(ele => {
        let axisText = getRangetxt(sheetIndex, { column: ele.column, row: ele.row });
        result.push(axisText);
    });

    return result;
}

/**
 * 返回指定工作表指定范围的单元格二维数组数据，每个单元格为一个对象
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 */
export function getRangeValue (options = {}) {
    let curOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        range,
        order = curOrder,
        showHidden = false
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (!range || typeof range === 'object') {
        return getdatabyselection(range, file.index, showHidden);
    } else if (typeof range === 'string') {
        if (formula.iscelldata(range)) {
            return getdatabyselection(formula.getcellrange(range), file.index, showHidden);
        } else {
            tooltip.info('The range is invalid, please check range parameter.', '');
        }
    }
}

/**
 * 复制指定工作表指定单元格区域的数据，返回包含`<table>`html格式的数据，可用于粘贴到excel中保持单元格样式。
 * @param {Object} options 可选参数
 * @param {Array | Object | String} options.range 选区范围
 * @param {order} options.order 工作表下标
 */
export function getRangeHtml (options = {}) {
    let {
        range = Store.luckysheet_select_save,
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };
    range = JSON.parse(JSON.stringify(range));

    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        let cellrange = formula.getcellrange(range);
        range = [{
            "row": cellrange.row,
            "column": cellrange.column
        }];
    } else if (getObjType(range) == 'object') {
        if (range.row == null || range.column == null) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        range = [{
            "row": range.row,
            "column": range.column
        }];
    }

    if (getObjType(range) != 'array') {
        return tooltip.info("The range parameter is invalid.", "");
    }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    // 复制范围内包含部分合并单元格，提示
    let cfg = $.extend(true, {}, file.config);
    if (cfg.merge != null) {
        let has_PartMC = false;

        for (let s = 0; s < range.length; s++) {
            let r1 = range[s].row[0],
                r2 = range[s].row[1];
            let c1 = range[s].column[0],
                c2 = range[s].column[1];

            has_PartMC = hasPartMC(cfg, r1, r2, c1, c2);

            if (has_PartMC) {
                break;
            }
        }

        if (has_PartMC) {
            return tooltip.info("Cannot perform this operation on partially merged cells", "");
        }
    }

    // 多重选区 有条件格式时 提示
    let cdformat = $.extend(true, [], file.luckysheet_conditionformat_save);
    if (range.length > 1 && cdformat.length > 0) {
        let hasCF = false;
        let cf_compute = conditionformat.getComputeMap(file.index);

        for (let s = 0; s < range.length; s++) {
            let r1 = range[s].row[0],
                r2 = range[s].row[1];
            let c1 = range[s].column[0],
                c2 = range[s].column[1];

            for (let r = r1; r <= r2; r++) {
                for (let c = c1; c <= c2; c++) {
                    if (conditionformat.checksCF(r, c, cf_compute) != null) {
                        hasCF = true;
                        break;
                    }
                }

                if (hasCF) {
                    break;
                }
            }

            if (hasCF) {
                break;
            }
        }

        if (hasCF) {
            return tooltip.info("Cannot perform this operation on multiple selection areas, please select a single area", "");
        }
    }

    // 多重选区 行不一样且列不一样时 提示
    if (range.length > 1) {
        let isSameRow = true,
            str_r = range[0].row[0],
            end_r = range[0].row[1];
        let isSameCol = true,
            str_c = range[0].column[0],
            end_c = range[0].column[1];

        for (let s = 1; s < range.length; s++) {
            if (range[s].row[0] != str_r || range[s].row[1] != end_r) {
                isSameRow = false;
            }

            if (range[s].column[0] != str_c || range[s].column[1] != end_c) {
                isSameCol = false;
            }
        }

        if ((!isSameRow && !isSameCol) || selectIsOverlap(range)) {
            return tooltip.info("Cannot perform this operation on multiple selection areas, please select a single area", "");
        }
    }

    let rowIndexArr = [], colIndexArr = [];

    for (let s = 0; s < range.length; s++) {
        let r1 = range[s].row[0],
            r2 = range[s].row[1];
        let c1 = range[s].column[0],
            c2 = range[s].column[1];

        for (let r = r1; r <= r2; r++) {
            if (cfg.rowhidden != null && cfg.rowhidden[r] != null) {
                continue;
            }

            if (!rowIndexArr.includes(r)) {
                rowIndexArr.push(r);
            }

            for (let c = c1; c <= c2; c++) {
                if (cfg.colhidden != null && cfg.colhidden[c] != null) {
                    continue;
                }

                if (!colIndexArr.includes(c)) {
                    colIndexArr.push(c);
                }
            }
        }
    }

    let borderInfoCompute;
    if (cfg.borderInfo && cfg.borderInfo.length > 0) { // 边框
        borderInfoCompute = getBorderInfoCompute(file.index);
    }

    let d = file.data;
    if (d == null || d.length == 0) {
        d = sheetmanage.buildGridData(file);
    }

    let cpdata = "";
    let colgroup = "";

    rowIndexArr = rowIndexArr.sort((a, b) => a - b);
    colIndexArr = colIndexArr.sort((a, b) => a - b);

    for (let i = 0; i < rowIndexArr.length; i++) {
        let r = rowIndexArr[i];

        if (cfg.rowhidden != null && cfg.rowhidden[r] != null) {
            continue;
        }

        cpdata += '<tr>';

        for (let j = 0; j < colIndexArr.length; j++) {
            let c = colIndexArr[j];

            if (cfg.colhidden != null && cfg.colhidden[c] != null) {
                continue;
            }

            let column = '<td ${span} style="${style}">';

            if (d[r] != null && d[r][c] != null) {
                let style = "", span = "";

                if (r == rowIndexArr[0]) {
                    if (cfg.columnlen == null || cfg.columnlen[c.toString()] == null) {
                        colgroup += '<colgroup width="72px"></colgroup>';
                    } else {
                        colgroup += '<colgroup width="' + cfg.columnlen[c.toString()] + 'px"></colgroup>';
                    }
                }

                if (c == colIndexArr[0]) {
                    if (cfg.rowlen == null || cfg.rowlen[r.toString()] == null) {
                        style += 'height:19px;';
                    } else {
                        style += 'height:' + cfg.rowlen[r.toString()] + 'px;';
                    }
                }

                let reg = /^(w|W)((0?)|(0\.0+))$/;
                let c_value;
                if (d[r][c].ct != null && d[r][c].ct.fa != null && d[r][c].ct.fa.match(reg)) {
                    c_value = getcellvalue(r, c, d);
                } else {
                    c_value = getcellvalue(r, c, d, "m");
                }

                style += menuButton.getStyleByCell(d, r, c);

                if (getObjType(d[r][c]) == "object" && ("mc" in d[r][c])) {
                    if ("rs" in d[r][c].mc) {
                        span = 'rowspan="' + d[r][c].mc.rs + '" colspan="' + d[r][c].mc.cs + '"';

                        // 边框
                        if (borderInfoCompute && borderInfoCompute[r + "_" + c]) {
                            let bl_obj = { "color": {}, "style": {} },
                                br_obj = { "color": {}, "style": {} },
                                bt_obj = { "color": {}, "style": {} },
                                bb_obj = { "color": {}, "style": {} };

                            for (let bd_r = r; bd_r < (r + d[r][c].mc.rs); bd_r++) {
                                for (let bd_c = c; bd_c < (c + d[r][c].mc.cs); bd_c++) {
                                    if (bd_r == r && borderInfoCompute[bd_r + "_" + bd_c] && borderInfoCompute[bd_r + "_" + bd_c].t) {
                                        let linetype = borderInfoCompute[bd_r + "_" + bd_c].t.style;
                                        let bcolor = borderInfoCompute[bd_r + "_" + bd_c].t.color;

                                        if (bt_obj.style[linetype] == null) {
                                            bt_obj.style[linetype] = 1;
                                        } else {
                                            bt_obj.style[linetype] = bt_obj.style[linetype] + 1;
                                        }

                                        if (bt_obj.color[bcolor] == null) {
                                            bt_obj.color[bcolor] = 1;
                                        } else {
                                            bt_obj.color[bcolor] = bt_obj.color[bcolor] + 1;
                                        }
                                    }

                                    if (bd_r == (r + d[r][c].mc.rs - 1) && borderInfoCompute[bd_r + "_" + bd_c] && borderInfoCompute[bd_r + "_" + bd_c].b) {
                                        let linetype = borderInfoCompute[bd_r + "_" + bd_c].b.style;
                                        let bcolor = borderInfoCompute[bd_r + "_" + bd_c].b.color;

                                        if (bb_obj.style[linetype] == null) {
                                            bb_obj.style[linetype] = 1;
                                        } else {
                                            bb_obj.style[linetype] = bb_obj.style[linetype] + 1;
                                        }

                                        if (bb_obj.color[bcolor] == null) {
                                            bb_obj.color[bcolor] = 1;
                                        } else {
                                            bb_obj.color[bcolor] = bb_obj.color[bcolor] + 1;
                                        }
                                    }

                                    if (bd_c == c && borderInfoCompute[bd_r + "_" + bd_c] && borderInfoCompute[bd_r + "_" + bd_c].l) {
                                        let linetype = borderInfoCompute[r + "_" + c].l.style;
                                        let bcolor = borderInfoCompute[bd_r + "_" + bd_c].l.color;

                                        if (bl_obj.style[linetype] == null) {
                                            bl_obj.style[linetype] = 1;
                                        } else {
                                            bl_obj.style[linetype] = bl_obj.style[linetype] + 1;
                                        }

                                        if (bl_obj.color[bcolor] == null) {
                                            bl_obj.color[bcolor] = 1;
                                        } else {
                                            bl_obj.color[bcolor] = bl_obj.color[bcolor] + 1;
                                        }
                                    }

                                    if (bd_c == (c + d[r][c].mc.cs - 1) && borderInfoCompute[bd_r + "_" + bd_c] && borderInfoCompute[bd_r + "_" + bd_c].r) {
                                        let linetype = borderInfoCompute[bd_r + "_" + bd_c].r.style;
                                        let bcolor = borderInfoCompute[bd_r + "_" + bd_c].r.color;

                                        if (br_obj.style[linetype] == null) {
                                            br_obj.style[linetype] = 1;
                                        } else {
                                            br_obj.style[linetype] = br_obj.style[linetype] + 1;
                                        }

                                        if (br_obj.color[bcolor] == null) {
                                            br_obj.color[bcolor] = 1;
                                        } else {
                                            br_obj.color[bcolor] = br_obj.color[bcolor] + 1;
                                        }
                                    }
                                }
                            }

                            let rowlen = d[r][c].mc.rs, collen = d[r][c].mc.cs;

                            if (JSON.stringify(bl_obj).length > 23) {
                                let bl_color = null, bl_style = null;

                                for (let x in bl_obj.color) {
                                    if (bl_obj.color[x] >= (rowlen / 2)) {
                                        bl_color = x;
                                    }
                                }

                                for (let x in bl_obj.style) {
                                    if (bl_obj.style[x] >= (rowlen / 2)) {
                                        bl_style = x;
                                    }
                                }

                                if (bl_color != null && bl_style != null) {
                                    style += "border-left:" + selection.getHtmlBorderStyle(bl_style, bl_color);
                                }
                            }

                            if (JSON.stringify(br_obj).length > 23) {
                                let br_color = null, br_style = null;

                                for (let x in br_obj.color) {
                                    if (br_obj.color[x] >= (rowlen / 2)) {
                                        br_color = x;
                                    }
                                }

                                for (let x in br_obj.style) {
                                    if (br_obj.style[x] >= (rowlen / 2)) {
                                        br_style = x;
                                    }
                                }

                                if (br_color != null && br_style != null) {
                                    style += "border-right:" + selection.getHtmlBorderStyle(br_style, br_color);
                                }
                            }

                            if (JSON.stringify(bt_obj).length > 23) {
                                let bt_color = null, bt_style = null;

                                for (let x in bt_obj.color) {
                                    if (bt_obj.color[x] >= (collen / 2)) {
                                        bt_color = x;
                                    }
                                }

                                for (let x in bt_obj.style) {
                                    if (bt_obj.style[x] >= (collen / 2)) {
                                        bt_style = x;
                                    }
                                }

                                if (bt_color != null && bt_style != null) {
                                    style += "border-top:" + selection.getHtmlBorderStyle(bt_style, bt_color);
                                }
                            }

                            if (JSON.stringify(bb_obj).length > 23) {
                                let bb_color = null, bb_style = null;

                                for (let x in bb_obj.color) {
                                    if (bb_obj.color[x] >= (collen / 2)) {
                                        bb_color = x;
                                    }
                                }

                                for (let x in bb_obj.style) {
                                    if (bb_obj.style[x] >= (collen / 2)) {
                                        bb_style = x;
                                    }
                                }

                                if (bb_color != null && bb_style != null) {
                                    style += "border-bottom:" + selection.getHtmlBorderStyle(bb_style, bb_color);
                                }
                            }
                        }
                    } else {
                        continue;
                    }
                } else {
                    // 边框
                    if (borderInfoCompute && borderInfoCompute[r + "_" + c]) {
                        // 左边框
                        if (borderInfoCompute[r + "_" + c].l) {
                            let linetype = borderInfoCompute[r + "_" + c].l.style;
                            let bcolor = borderInfoCompute[r + "_" + c].l.color;
                            style += "border-left:" + selection.getHtmlBorderStyle(linetype, bcolor);
                        }

                        // 右边框
                        if (borderInfoCompute[r + "_" + c].r) {
                            let linetype = borderInfoCompute[r + "_" + c].r.style;
                            let bcolor = borderInfoCompute[r + "_" + c].r.color;
                            style += "border-right:" + selection.getHtmlBorderStyle(linetype, bcolor);
                        }

                        // 下边框
                        if (borderInfoCompute[r + "_" + c].b) {
                            let linetype = borderInfoCompute[r + "_" + c].b.style;
                            let bcolor = borderInfoCompute[r + "_" + c].b.color;
                            style += "border-bottom:" + selection.getHtmlBorderStyle(linetype, bcolor);
                        }

                        // 上边框
                        if (borderInfoCompute[r + "_" + c].t) {
                            let linetype = borderInfoCompute[r + "_" + c].t.style;
                            let bcolor = borderInfoCompute[r + "_" + c].t.color;
                            style += "border-top:" + selection.getHtmlBorderStyle(linetype, bcolor);
                        }
                    }
                }

                column = replaceHtml(column, { "style": style, "span": span });

                if (c_value == null) {
                    c_value = getcellvalue(r, c, d);
                }

                if (c_value == null) {
                    c_value = " ";
                }

                column += c_value;
            } else {
                let style = "";

                // 边框
                if (borderInfoCompute && borderInfoCompute[r + "_" + c]) {
                    // 左边框
                    if (borderInfoCompute[r + "_" + c].l) {
                        let linetype = borderInfoCompute[r + "_" + c].l.style;
                        let bcolor = borderInfoCompute[r + "_" + c].l.color;
                        style += "border-left:" + selection.getHtmlBorderStyle(linetype, bcolor);
                    }

                    // 右边框
                    if (borderInfoCompute[r + "_" + c].r) {
                        let linetype = borderInfoCompute[r + "_" + c].r.style;
                        let bcolor = borderInfoCompute[r + "_" + c].r.color;
                        style += "border-right:" + selection.getHtmlBorderStyle(linetype, bcolor);
                    }

                    // 下边框
                    if (borderInfoCompute[r + "_" + c].b) {
                        let linetype = borderInfoCompute[r + "_" + c].b.style;
                        let bcolor = borderInfoCompute[r + "_" + c].b.color;
                        style += "border-bottom:" + selection.getHtmlBorderStyle(linetype, bcolor);
                    }

                    // 上边框
                    if (borderInfoCompute[r + "_" + c].t) {
                        let linetype = borderInfoCompute[r + "_" + c].t.style;
                        let bcolor = borderInfoCompute[r + "_" + c].t.color;
                        style += "border-top:" + selection.getHtmlBorderStyle(linetype, bcolor);
                    }
                }

                column += "";

                if (r == rowIndexArr[0]) {
                    if (cfg.columnlen == null || cfg.columnlen[c.toString()] == null) {
                        colgroup += '<colgroup width="72px"></colgroup>';
                    } else {
                        colgroup += '<colgroup width="' + cfg.columnlen[c.toString()] + 'px"></colgroup>';
                    }
                }

                if (c == colIndexArr[0]) {
                    if (cfg.rowlen == null || cfg.rowlen[r.toString()] == null) {
                        style += 'height:19px;';
                    } else {
                        style += 'height:' + cfg.rowlen[r.toString()] + 'px;';
                    }
                }

                column = replaceHtml(column, { "style": style, "span": "" });
                column += " ";
            }

            column += '</td>';
            cpdata += column;
        }

        cpdata += "</tr>";
    }

    cpdata = '<table data-type="luckysheet_copy_action_table">' + colgroup + cpdata + '</table>';

    return cpdata;
}

/**
 * 复制指定工作表指定单元格区域的数据，返回一维、二维或者自定义行列数的二维数组的数据。只有在dimensional设置为custom的时候，才需要设置setting中的row和column
 * @param {String} dimensional 数组维度。可选值为：oneDimensional-一维数组；twoDimensional-二维数组； custom-自定义行列数的二维数组
 * @param {Object} options 可选参数
 * @param {Number} options.row dimensional为custom的时候设置，多维数组的行数
 * @param {Number} options.column dimensional为custom的时候设置，多维数组的列数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 */
export function getRangeArray (dimensional, options = {}) {
    let dimensionalValues = ['oneDimensional', 'twoDimensional'];

    if (!dimensionalValues.includes(dimensional)) {
        return tooltip.info("The dimensional parameter is invalid.", "");
    }

    let {
        range = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1],
        order = getSheetIndex(Store.currentSheetIndex)
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        range = formula.getcellrange(range);
    }

    if (getObjType(range) != 'object' || range.row == null || range.column == null) {
        return tooltip.info("The range parameter is invalid.", "");
    }

    let r1 = range.row[0],
        r2 = range.row[1];
    let c1 = range.column[0],
        c2 = range.column[1];

    // 复制范围内包含部分合并单元格，提示
    let cfg = $.extend(true, {}, file.config);
    if (cfg.merge != null) {
        let has_PartMC = hasPartMC(cfg, r1, r2, c1, c2);

        if (has_PartMC) {
            return tooltip.info("Cannot perform this operation on partially merged cells", "");
        }
    }

    let data = file.data;
    if (data == null || data.length == 0) {
        data = sheetmanage.buildGridData(file);
    }

    let dataArr = [];

    if (dimensional == 'oneDimensional') { // 一维数组
        for (let r = r1; r <= r2; r++) {
            for (let c = c1; c <= c2; c++) {
                let cell = data[r][c];

                if (cell == null || cell.v == null) {
                    dataArr.push(null);
                } else {
                    dataArr.push(cell.v);
                }
            }
        }
    } else if (dimensional == 'twoDimensional') {
        for (let r = r1; r <= r2; r++) {
            let row = [];

            for (let c = c1; c <= c2; c++) {
                let cell = data[r][c];

                if (cell == null || cell.v == null) {
                    row.push(null);
                } else {
                    row.push(cell.v);
                }
            }

            dataArr.push(row);
        }
    }

    return dataArr;
}

/**
 * 复制指定工作表指定单元格区域的数据，返回json格式的数据
 * @param {Boolean} isFirstRowTitle 是否首行为标题
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 */
export function getRangeJson (isFirstRowTitle, options = {}) {
    let curRange = Store.luckysheet_select_save[0];
    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        range = curRange,
        order = curSheetOrder
    } = { ...options };
    let file = Store.luckysheetfile[order];
    let config = file.config;

    if (range && typeof range === 'string' && formula.iscelldata(range)) {
        range = formula.getcellrange(range);
    }

    if (!range || range.length > 1) {
        if (isEditMode()) {
            alert(locale_drag.noMulti);
        } else {
            tooltip.info(locale_drag.noMulti, "");
        }
        return;
    }

    // 复制范围内包含部分合并单元格，提示
    if (config.merge != null) {
        let has_PartMC = false;
        let r1 = range.row[0],
            r2 = range.row[1],
            c1 = range.column[0],
            c2 = range.column[1];
        has_PartMC = hasPartMC(config, r1, r2, c1, c2);

        if (has_PartMC) {
            if (isEditMode()) {
                alert(locale().drag.noPartMerge);
            } else {
                tooltip.info(locale().drag.noPartMerge, "");
            }
            return;
        }
    }
    let getdata = getdatabyselection(range, file.index);
    let arr = [];
    if (getdata.length === 0) {
        return;
    }
    if (isFirstRowTitle) {
        if (getdata.length === 1) {
            let obj = {};
            for (let i = 0; i < getdata[0].length; i++) {
                obj[getcellvalue(0, i, getdata)] = "";
            }
            arr.push(obj);
        } else {
            for (let r = 1; r < getdata.length; r++) {
                let obj = {};
                for (let c = 0; c < getdata[0].length; c++) {
                    if (getcellvalue(0, c, getdata) == undefined) {
                        obj[""] = getcellvalue(r, c, getdata);
                    } else {
                        obj[getcellvalue(0, c, getdata)] = getcellvalue(r, c, getdata);
                    }
                }
                arr.push(obj);
            }
        }
    } else {
        let st = range.column[0];
        for (let r = 0; r < getdata.length; r++) {
            let obj = {};
            for (let c = 0; c < getdata[0].length; c++) {
                obj[chatatABC(c + st)] = getcellvalue(r, c, getdata);
            }
            arr.push(obj);
        }
    }
    // selection.copybyformat(new Event('click'), JSON.stringify(arr));
    return arr;
}

/**
 *
 * @param {String} type 对角线还是对角线偏移 "normal"-对角线  "anti"-反对角线
"offset"-对角线偏移
 * @param {Object} options 可选参数
 * @param {Number} options.column type为offset的时候设置，对角偏移的列数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 */
export function getRangeDiagonal (type, options = {}) {
    let typeValues = ['normal', 'anti', 'offset'];
    if (typeValues.indexOf(type) < 0) {
        return tooltip.info('The type parameter must be included in [\'normal\', \'anti\', \'offset\']', '');
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let curRange = JSON.parse(JSON.stringify(Store.luckysheet_select_save));
    let {
        column = 1,
        range = curRange,
        order = curSheetOrder
    } = { ...options };

    let file = Store.luckysheetfile[order];
    let config = file.config;

    if (range && typeof range === 'string' && formula.iscelldata(range)) {
        range = formula.getcellrange(range);
    }

    if (!range || range.length > 1) {
        if (isEditMode()) {
            alert(locale().drag.noMulti);
        } else {
            tooltip.info(locale().drag.noMulti, "");
        }
        return;
    }

    // 复制范围内包含部分合并单元格，提示
    if (config.merge != null) {
        let has_PartMC = false;
        let r1 = range[0].row[0],
            r2 = range[0].row[1],
            c1 = range[0].column[0],
            c2 = range[0].column[1];
        has_PartMC = hasPartMC(config, r1, r2, c1, c2);

        if (has_PartMC) {
            if (isEditMode()) {
                alert(locale().drag.noPartMerge);
            } else {
                tooltip.info(locale().drag.noPartMerge, "");
            }
            return;
        }
    }
    let getdata = getdatabyselection(range, order);
    let arr = [];
    if (getdata.length === 0) {
        return;
    }

    let clen = getdata[0].length;
    switch (type) {
    case 'normal':
        for (let r = 0; r < getdata.length; r++) {
            if (r >= clen) {
                break;
            }
            arr.push(getdata[r][r]);
        }
        break;
    case 'anti':
        for (let r = 0; r < getdata.length; r++) {
            if (r >= clen) {
                break;
            }
            arr.push(getdata[r][clen - r - 1]);
        }
        break;
    case 'offset':
        if (column.toString() == "NaN") {
            if (isEditMode()) {
                alert(locale().drag.inputCorrect);
            } else {
                tooltip.info(locale().drag.inputCorrect, "");
            }
            return;
        }

        if (column < 0) {
            if (isEditMode()) {
                alert(locale().drag.offsetColumnLessZero);
            } else {
                tooltip.info(locale().drag.offsetColumnLessZero, "");
            }
            return;
        }

        for (let r = 0; r < getdata.length; r++) {
            if (r + column >= clen) {
                break;
            }
            arr.push(getdata[r][r + column]);
        }
        break;
    }
    selection.copybyformat(new Event(), JSON.stringify(arr));
}

/**
 * 复制指定工作表指定单元格区域的数据，返回布尔值的数据
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 */
export function getRangeBoolean (options = {}) {
    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let curRange = JSON.parse(JSON.stringify(Store.luckysheet_select_save));
    let {
        range = curRange,
        order = curSheetOrder
    } = { ...options };

    let file = Store.luckysheetfile[order];
    let config = file.config;

    if (range && typeof range === 'string' && formula.iscelldata(range)) {
        range = formula.getcellrange(range);
    }

    if (!range || range.length > 1) {
        if (isEditMode()) {
            alert(locale().drag.noMulti);
        } else {
            tooltip.info(locale().drag.noMulti, "");
        }
        return;
    }

    // 复制范围内包含部分合并单元格，提示
    if (config.merge != null) {
        let has_PartMC = false;
        let r1 = range[0].row[0],
            r2 = range[0].row[1],
            c1 = range[0].column[0],
            c2 = range[0].column[1];
        has_PartMC = hasPartMC(config, r1, r2, c1, c2);

        if (has_PartMC) {
            if (isEditMode()) {
                alert(locale().drag.noPartMerge);
            } else {
                tooltip.info(locale().drag.noPartMerge, "");
            }
            return;
        }
    }
    let getdata = getdatabyselection(range, order);
    let arr = [];
    if (getdata.length === 0) {
        return;
    }
    for (let r = 0; r < getdata.length; r++) {
        let a = [];
        for (let c = 0; c < getdata[0].length; c++) {
            let bool = false;

            let v;
            if (getObjType(getdata[r][c]) == "object") {
                v = getdata[r][c].v;
            } else {
                v = getdata[r][c];
            }

            if (v == null || v == "") {
                bool = false;
            } else {
                v = parseInt(v);
                if (v == null || v > 0) {
                    bool = true;
                } else {
                    bool = false;
                }
            }
            a.push(bool);
        }
        arr.push(a);
    }

    selection.copybyformat(event, JSON.stringify(arr));
}

/**
 * 指定工作表选中一个或多个选区为选中状态并选择是否高亮，支持多种格式设置。
 * @param {Array | Object | String} range 选区范围
 * @param {Object} options 可选参数
 * @param {Boolean} options.show 是否显示高亮选中效果；默认值为 `true`
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setRangeShow (range, options = {}) {
    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        let cellrange = formula.getcellrange(range);
        range = [{
            "row": cellrange.row,
            "column": cellrange.column
        }];
    } else if (getObjType(range) == 'object') {
        if (range.row == null || range.column == null) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        range = [{
            "row": range.row,
            "column": range.column
        }];
    }

    if (getObjType(range) == 'array') {
        for (let i = 0; i < range.length; i++) {
            if (getObjType(range[i]) === 'string') {
                if (!formula.iscelldata(range[i])) {
                    return tooltip.info("The range parameter is invalid.", "");
                }
                let cellrange = formula.getcellrange(range[i]);
                range[i] = {
                    "row": cellrange.row,
                    "column": cellrange.column
                };
            } else if (getObjType(range) == 'object') {
                if (range.row == null || range.column == null) {
                    return tooltip.info("The range parameter is invalid.", "");
                }
                range = {
                    "row": range.row,
                    "column": range.column
                };
            }
        }
    }

    if (getObjType(range) != 'array') {
        return tooltip.info("The range parameter is invalid.", "");
    }

    let {
        show = true,
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }
    const { noMerge } = options;
    if (!noMerge) {
        for (let i = 0; i < range.length; i++) {
            let changeparam = menuButton.mergeMoveMain(range[i].column, range[i].row, range[i]);
            if (changeparam) {
                range[i] = {
                    "row": changeparam[1],
                    "column": changeparam[0]
                };
            }
        }
    }
    file.luckysheet_select_save = range;

    if (file.index == Store.currentSheetIndex) {
        Store.luckysheet_select_save = range;
        selectHightlightShow();

        if (!show) {
            $("#luckysheet-cell-selected-boxs").hide();
            $("#luckysheet-cell-selected-focus").hide();
            $("#luckysheet-row-count-show").hide();
            $("#luckysheet-column-count-show").hide();
            $("#luckysheet-rows-h-selected").empty();
            $("#luckysheet-cols-h-selected").empty();
        }
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 将一个单元格数组数据赋值到指定的区域，数据格式同getRangeValue方法取到的数据。
 * @param {Array[Array]} data 要赋值的单元格二维数组数据，每个单元格的值，可以为字符串或数字，或为符合Luckysheet格式的对象
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Boolean} options.isRefresh 是否刷新界面；默认为true
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setRangeValue (data, options = {}) {
    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let curRange = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1];
    let {
        range = curRange,
        isRefresh = true,
        isRefreshGrid = true,
        isFormat = false,
        order = curSheetOrder,
        success,
        callHookAfterBatchProcess = false
    } = { ...options };

    if (data == null) {
        return tooltip.info('The data which will be set to range cannot be null.', '');
    }

    if (range instanceof Array) {
        return tooltip.info('setRangeValue only supports a single selection.', '');
    }

    if (typeof range === 'string' && formula.iscelldata(range)) {
        range = formula.getcellrange(range);
    }

    let rowCount = range.row[1] - range.row[0] + 1,
        columnCount = range.column[1] - range.column[0] + 1;
    if (data.length !== rowCount || data[0].length !== columnCount) {
        return tooltip.info('The data to be set does not match the selection.', '');
    }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }
    let oldData = [];
    let startTime = Date.now();
    for (let i = 0; i < rowCount; i++) {
        for (let j = 0; j < columnCount; j++) {
            let row = range.row[0] + i,
                column = range.column[0] + j;
            if (isFormat) {
                setcellvalue(row, column, file.data, {
                    ...file.data[row][column],
                    ...data[i][j]
                });
            } else {
                setCellValue(row, column, data[i][j], {
                    order: order,
                    isRefresh: false,
                    isFormat,
                    triggerHook: !callHookAfterBatchProcess,
                    success: (data, oldValue) => {
                        oldData.push({
                            row,
                            column,
                            oldValue
                        });
                    }
                });
            }
        }
    }
    // 当 callHookAfterBatchProcess 为 true 时 cellUpdated 回调统一执行
    if (callHookAfterBatchProcess) {
        setTimeout(() => {
            // Hook function
            oldData.forEach((cell) => {
                const { row, column, oldValue } = cell;
                if (oldValue && oldValue.ct &&  oldValue.ct.t === 'inlineStr') {
                    oldValue.m = oldValue.v;
                }
                let updateValue = file.data[row][column]
                    ? {
                        ...file.data[row][column]
                    }
                    : null;
                if (updateValue && updateValue.ct && updateValue.ct.t === 'inlineStr') {
                    updateValue.m = updateValue.v;
                }
                method.createHookFunction("cellUpdated", row, column, oldValue, updateValue, order);
            });
        }, 0);
    }

    if (isRefreshGrid) {
        if (file.index == Store.currentSheetIndex) {
            let fileData = file.data;
            jfrefreshgrid(fileData, [{
                row: range.row,
                column: range.column
            }], undefined, true, false);
        }

        if (isRefresh) {
            luckysheetrefreshgrid();
        }
    }
    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 设置指定范围的单元格格式，一般用作处理格式，赋值操作推荐使用setRangeValue方法
 * @param {String} attr 要赋值的单元格二维数组数据，每个单元格的值，可以为字符串或数字，或为符合Luckysheet格式的对象
 * @param {Number | String | Object} value 具体的设置值
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 设置参数的目标选区范围，支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 */
export function setSingleRangeFormat (attr, value, options = {}) {
    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let curRange = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1];
    let {
        range = curRange,
        order = curSheetOrder,
        isRefresh = true,
        isRefreshGrid = true
    } = { ...options };

    if (!attr) {
        tooltip.info('Arguments attr cannot be null or undefined.', '');
        return 'error';
    }

    if (range instanceof Array) {
        tooltip.info('setRangeValue only supports a single selection.', '');
        return 'error';
    }

    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            tooltip.info("The range parameter is invalid.", "");
            return 'error';
        }

        range = formula.getcellrange(range);
    }

    if (getObjType(range) != 'object' || range.row == null || range.column == null) {
        tooltip.info("The range parameter is invalid.", "");
        return 'error';
    }
    let file = Store.luckysheetfile[order];
    const d_r = file.data.length - 1;
    const d_c = file.data[0].length - 1;
    let loadedRange;
    let unloadRange;
    if (range.row.length > 1) {
        // 当参数区域完全为未加载数据
        if (d_r < range.row[0]) {
            unloadRange = {
                "row": range.row,
                "column": range.column
            };
        }
        // 参数区域分布在已加载区域和未加载区域
        if (d_r >= range.row[0] && d_r < range.row[1]) {
            loadedRange = {
                "row": [range.row[0], d_r],
                "column": range.column
            };
            unloadRange = {
                "row": [d_r + 1, range.row[1]],
                "column": range.column
            };
        }
        // 参数区域完全为已加载数据
        if (d_r >= range.row[1]) {
            loadedRange = {
                "row": range.row,
                "column": range.column
            };
        }
    }

    if (attr === 'bd') {
        file.config.borderInfo.push({
            ...value,
            range: [range],
            rangeType: 'range'
        });
    }

    if (loadedRange && loadedRange.row) {
        let attrData = new Array(loadedRange.row[1] - loadedRange.row[0] + 1).fill({
            [attr]: value
        }).map(v => new Array(loadedRange.column[1] - loadedRange.column[0] + 1).fill({
            [attr]: value
        }));
        setRangeValue(attrData, {
            range: {
                row: [loadedRange.row[0], loadedRange.row[1]],
                column: [loadedRange.column[0], loadedRange.column[1]]
            },
            callHookAfterBatchProcess: true,
            isFormat: true,
            isRefresh,
            isRefreshGrid,
            order
        });
    }
}

/**
 * 设置指定范围的单元格格式，一般用作处理格式。支持多选区设置
 * @param {String} attr 要赋值的单元格二维数组数据，每个单元格的值，可以为字符串或数字，或为符合Luckysheet格式的对象
 * @param {Number | String | Object} value 具体的设置值
 * @param {Object} options 可选参数
 * @param {Array | Object | String} options.range 设置参数的目标选区范围，支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setRangeFormat (attr, value, options = {}) {
    let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let curRange = JSON.parse(JSON.stringify(Store.luckysheet_select_save));
    let {
        range = curRange,
        order = curSheetOrder,
        success
    } = { ...options };
    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        let cellrange = formula.getcellrange(range);
        range = [{
            "row": cellrange.row,
            "column": cellrange.column
        }];
    } else if (getObjType(range) == 'object') {
        if (range.row == null || range.column == null) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        range = [{
            "row": range.row,
            "column": range.column
        }];
    }

    if (getObjType(range) != 'array') {
        return tooltip.info("The range parameter is invalid.", "");
    }

    let file = Store.luckysheetfile[order];

    const d_r = file.data.length - 1;
    const d_c = file.data[0].length - 1;
    let result = [];
    for (let i = 0; i < range.length; i++) {
        result.push(setSingleRangeFormat(attr, value, { range: range[i], order: order, isRefreshGrid: false }));
    }
    if (file.index == Store.currentSheetIndex) {
        jfrefreshgrid(file.data, undefined, undefined, false, true);
    }
    luckysheetrefreshgrid();
    if (success && typeof success === 'function') {
    }
}

/**
 * 为指定索引的工作表，选定的范围开启或关闭筛选功能
 * @param {String} type 打开还是关闭筛选功能  open-打开筛选功能，返回当前筛选的范围对象；close-关闭筛选功能，返回关闭前筛选的范围对象
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Object} options.success 操作结束的回调函数
 */
export function setRangeFilter (type, options = {}) {
    let typeValues = ['open', 'close'];

    if (!typeValues.includes(type)) {
        return tooltip.info("The type parameter is invalid.", "");
    }

    let {
        range = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1],
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        range = formula.getcellrange(range);
    }

    if (getObjType(range) != 'object' || range.row == null || range.column == null) {
        return tooltip.info("The range parameter is invalid.", "");
    }

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    if (type == 'open') {
        file.filter_select = range;

        if (file.index == Store.currentSheetIndex) {
            createFilterOptions(range, file.filter);
        }

        return {
            "row": range.row,
            "column": range.column
        };
    } else if (type == 'close') {
        let luckysheet_filter_save = $.extend(true, {}, file.filter_select);

        file.filter_select = null;

        $("#luckysheet-filter-selected-sheet" + file.index).remove();
        $("#luckysheet-filter-options-sheet" + file.index).remove();

        return {
            "row": luckysheet_filter_save.row,
            "column": luckysheet_filter_save.column
        };
    }
}

/**
 * 为指定索引的工作表，选定的范围设定合并单元格
 * @param {String} type 合并类型 all-全部合并  horizontal-水平合并  vertical-垂直合并
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Object} options.success 操作结束的回调函数
 */
export function setRangeMerge (type, options = {}) {
    let typeValues = ['all', 'horizontal', 'vertical'];
    if (typeValues.indexOf(type) < 0) {
        return tooltip.info('The type parameter must be included in [\'all\', \'horizontal\', \'vertical\']', '');
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex),
        curRange = JSON.parse(JSON.stringify(Store.luckysheet_select_save));
    let {
        range = curRange,
        order = curSheetOrder,
        success
    } = { ...options };

    let file = Store.luckysheetfile[order],
        cfg = file.config,
        data = file.data;

    if (data.length == 0) {
        data = $.extend(true, [], sheetmanage.buildGridData(file));
    }

    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info('Incorrect selection format', '');
        }

        let cellrange = formula.getcellrange(range);
        range = [{
            "row": cellrange.row,
            "column": cellrange.column
        }];
    } else if (getObjType(range) == 'object') {
        if (!range.hasOwnProperty("row") || !range.hasOwnProperty("column")) {
            return tooltip.info('Incorrect selection format', '');
        }

        range = [{
            "row": range.row,
            "column": range.column
        }];
    }

    // 不能合并重叠区域
    if (selectIsOverlap(range)) {
        return tooltip.info('Cannot merge overlapping range', '');
    }

    // 选区是否含有 部分合并单元格
    if (cfg.merge != null) {
        let has_PartMC = false;

        for (let s = 0; s < range.length; s++) {
            let r1 = range[s].row[0],
                r2 = range[s].row[1];
            let c1 = range[s].column[0],
                c2 = range[s].column[1];

            has_PartMC = hasPartMC(cfg, r1, r2, c1, c2, order);

            if (has_PartMC) {
                break;
            }
        }

        if (has_PartMC) {
            return tooltip.info('Cannot perform this operation on partially merged cells', '');
        }
    } else {
        cfg.merge = {};
    }

    // 选区是否含有 合并的单元格
    let isHasMc = false;

    for (let i = 0; i < range.length; i++) {
        let r1 = range[i].row[0],
            r2 = range[i].row[1];
        let c1 = range[i].column[0],
            c2 = range[i].column[1];

        for (let r = r1; r <= r2; r++) {
            for (let c = c1; c <= c2; c++) {
                let cell = data[r][c];

                if (getObjType(cell) == "object" && ("mc" in cell)) {
                    isHasMc = true;
                    break;
                }
            }

            if (isHasMc) {
                break;
            }
        }
    }

    if (isHasMc) { // 选区有合并单元格（选区都执行 取消合并）
        cancelRangeMerge({
            range: range,
            order: order
        });
    } else {
        for (let i = 0; i < range.length; i++) {
            let r1 = range[i].row[0],
                r2 = range[i].row[1];
            let c1 = range[i].column[0],
                c2 = range[i].column[1];

            if (r1 == r2 && c1 == c2) {
                continue;
            }

            if (type == "all") {
                let fv = {}, isfirst = false;

                for (let r = r1; r <= r2; r++) {
                    for (let c = c1; c <= c2; c++) {
                        let cell = data[r][c];

                        if (cell != null && (!isRealNull(cell.v) || cell.f != null) && !isfirst) {
                            fv = $.extend(true, {}, cell);
                            isfirst = true;
                        }

                        data[r][c] = { "mc": { "r": r1, "c": c1 } };
                    }
                }

                data[r1][c1] = fv;
                data[r1][c1].mc = { "r": r1, "c": c1, "rs": r2 - r1 + 1, "cs": c2 - c1 + 1 };

                cfg.merge[r1 + "_" + c1] = { "r": r1, "c": c1, "rs": r2 - r1 + 1, "cs": c2 - c1 + 1 };
            } else if (type == "vertical") {
                for (let c = c1; c <= c2; c++) {
                    let fv = {}, isfirst = false;

                    for (let r = r1; r <= r2; r++) {
                        let cell = data[r][c];

                        if (cell != null && (!isRealNull(cell.v) || cell.f != null) && !isfirst) {
                            fv = $.extend(true, {}, cell);
                            isfirst = true;
                        }

                        data[r][c] = { "mc": { "r": r1, "c": c } };
                    }

                    data[r1][c] = fv;
                    data[r1][c].mc = { "r": r1, "c": c, "rs": r2 - r1 + 1, "cs": 1 };

                    cfg.merge[r1 + "_" + c] = { "r": r1, "c": c, "rs": r2 - r1 + 1, "cs": 1 };
                }
            } else if (type == "horizontal") {
                for (let r = r1; r <= r2; r++) {
                    let fv = {}, isfirst = false;

                    for (let c = c1; c <= c2; c++) {
                        let cell = data[r][c];

                        if (cell != null && (!isRealNull(cell.v) || cell.f != null) && !isfirst) {
                            fv = $.extend(true, {}, cell);
                            isfirst = true;
                        }

                        data[r][c] = { "mc": { "r": r, "c": c1 } };
                    }

                    data[r][c1] = fv;
                    data[r][c1].mc = { "r": r, "c": c1, "rs": 1, "cs": c2 - c1 + 1 };

                    cfg.merge[r + "_" + c1] = { "r": r, "c": c1, "rs": 1, "cs": c2 - c1 + 1 };
                }
            }
        }

        if (order == curSheetOrder) {
            if (Store.clearjfundo && !Store.suspend) {
                Store.jfundo.length  = 0;
                Store.jfredo.push({
                    "type": "mergeChange",
                    "sheetIndex": file.index,
                    "data": $.extend(true, [], file.data),
                    "curData": data,
                    "range": range,
                    "config": $.extend(true, {}, file.config),
                    "curConfig": cfg
                });
            }

            Store.clearjfundo = false;
            jfrefreshgrid(data, range, { "cfg": cfg });
            Store.clearjfundo = true;
        } else {
            file.data = data;
            file.config = cfg;
        }
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 为指定索引的工作表，选定的范围取消合并单元格
 * @param {Object} options 可选参数
 * @param {Array | Object | String} options.range 选区范围
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Object} options.success 操作结束的回调函数
 */
export function cancelRangeMerge (options = {}) {
    let curRange = Store.luckysheet_select_save,
        curSheetOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        range = curRange,
        order = curSheetOrder,
        success
    } = { ...options };

    let file = Store.luckysheetfile[order],
        cfg = file.config,
        data = file.data;

    if (data.length == 0) {
        data = $.extend(true, [], sheetmanage.buildGridData(file));
    }

    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info('Incorrect selection format', '');
        }

        let cellrange = formula.getcellrange(range);
        range = [{
            "row": cellrange.row,
            "column": cellrange.column
        }];
    } else if (getObjType(range) == 'object') {
        if (!range.hasOwnProperty("row") || !range.hasOwnProperty("column")) {
            return tooltip.info('Incorrect selection format', '');
        }

        range = [{
            "row": range.row,
            "column": range.column
        }];
    }

    // 不能合并重叠区域
    if (selectIsOverlap(range)) {
        return tooltip.info('Cannot merge overlapping range', '');
    }

    // 选区是否含有 部分合并单元格
    if (cfg.merge != null) {
        let has_PartMC = false;

        for (let s = 0; s < range.length; s++) {
            let r1 = range[s].row[0],
                r2 = range[s].row[1];
            let c1 = range[s].column[0],
                c2 = range[s].column[1];

            has_PartMC = hasPartMC(cfg, r1, r2, c1, c2);

            if (has_PartMC) {
                break;
            }
        }

        if (has_PartMC) {
            return tooltip.info('Cannot perform this operation on partially merged cells', '');
        }
    }

    for (let i = 0; i < range.length; i++) {
        let r1 = range[i].row[0],
            r2 = range[i].row[1];
        let c1 = range[i].column[0],
            c2 = range[i].column[1];

        if (r1 == r2 && c1 == c2) {
            continue;
        }

        let fv = {};

        for (let r = r1; r <= r2; r++) {
            for (let c = c1; c <= c2; c++) {
                let cell = data[r][c];

                if (cell != null && cell.mc != null) {
                    let mc_r = cell.mc.r, mc_c = cell.mc.c;

                    if ("rs" in cell.mc) {
                        delete cell.mc;
                        delete cfg.merge[mc_r + "_" + mc_c];

                        fv[mc_r + "_" + mc_c] = $.extend(true, {}, cell);
                    } else {
                        // let cell_clone = fv[mc_r + "_" + mc_c];
                        let cell_clone = JSON.parse(JSON.stringify(fv[mc_r + "_" + mc_c] || {}));

                        delete cell_clone.v;
                        delete cell_clone.m;
                        delete cell_clone.ct;
                        delete cell_clone.f;
                        delete cell_clone.spl;

                        data[r][c] = cell_clone;
                    }
                }
            }
        }
    }

    if (order == curSheetOrder) {
        if (Store.clearjfundo && !Store.suspend) {
            Store.jfundo.length  = 0;
            Store.jfredo.push({
                "type": "mergeChange",
                "sheetIndex": file.index,
                "data": $.extend(true, [], file.data),
                "curData": data,
                "range": range,
                "config": $.extend(true, {}, file.config),
                "curConfig": cfg
            });
        }

        Store.clearjfundo = false;
        jfrefreshgrid(data, range, { "cfg": cfg });
        Store.clearjfundo = true;
    } else {
        file.data = data;
        file.config = cfg;
    }
}

/**
 * 为指定索引的工作表，选定的范围开启排序功能，返回选定范围排序后的数据。
 * @param {String} type 排序类型 asc-升序 desc-降序
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setRangeSort (type, options = {}) {
    let typeValues = ['asc', 'desc'];
    if (typeValues.indexOf(type) < 0) {
        return tooltip.info('The type parameter must be included in [\'asc\', \'desc\'', '');
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex),
        curRange = Store.luckysheet_select_save[0];
    let {
        range = curRange,
        order = curSheetOrder,
        success
    } = { ...options };

    let file = Store.luckysheetfile[order],
        cfg = $.extend(true, {}, file.config),
        fileData = $.extend(true, [], file.data);

    if (fileData.length == 0) {
        fileData = $.extend(true, [], sheetmanage.buildGridData(file));
    }

    if (range instanceof Array && range.length > 1) {
        tooltip.info(locale().sort.noRangeError, "");
        return;
    }

    if (range && typeof range === 'string' && formula.iscelldata(range)) {
        range = formula.getcellrange(range);
    }

    let r1 = range.row[0],
        r2 = range.row[1],
        c1 = range.column[0],
        c2 = range.column[1];

    let hasMc = false; // Whether the sort selection has merged cells
    let data = [];
    for (let r = r1; r <= r2; r++) {
        let data_row = [];
        for (let c = c1; c <= c2; c++) {
            if (fileData[r][c] != null && fileData[r][c].mc != null) {
                hasMc = true;
                break;
            }
            data_row.push(fileData[r][c]);
        }
        data.push(data_row);
    }

    if (hasMc) {
        Store.message && Store.message.info(locale().sort.mergeError);
        return;
    }

    data = orderbydata([].concat(data), 0, type === 'asc');

    for (let r = r1; r <= r2; r++) {
        for (let c = c1; c <= c2; c++) {
            fileData[r][c] = data[r - r1][c - c1];
        }
    }

    let allParam = {};
    if (cfg.rowlen != null) {
        cfg = rowlenByRange(fileData, r1, r2, cfg);

        allParam = {
            "cfg": cfg,
            "RowlChange": true
        };
    }

    if (file.index == Store.currentSheetIndex) {
        jfrefreshgrid(fileData, [{ "row": [r1, r2], "column": [c1, c2] }], allParam);
    } else {
        file.data = fileData;
        file.config = cfg;
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 为指定索引的工作表，选定的范围开启多列自定义排序功能，返回选定范围排序后的数据。
 * @param {Boolean} hasTitle 数据是否具有标题行
 * @param {Array} sort 列设置，设置需要排序的列索引和排序方式，格式如：[{ i:0,sort:'asc' },{ i:1,sort:'des' }]
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setRangeSortMulti (hasTitle, sort, options = {}) {
    if (!sort || !(sort instanceof Array)) {
        return tooltip.info('The sort parameter is invalid.', '');
    }

    let curSheetOrder = getSheetIndex(Store.currentSheetIndex),
        curRange = Store.luckysheet_select_save[0];
    let {
        range = curRange,
        order = curSheetOrder,
        success
    } = { ...options };

    let file = Store.luckysheetfile[order],
        cfg = $.extend(true, {}, file.config),
        fileData = $.extend(true, [], file.data);

    if (fileData.length == 0) {
        fileData = $.extend(true, [], sheetmanage.buildGridData(file));
    }

    if (range instanceof Array && range.length > 1) {
        tooltip.info(locale().sort.noRangeError, "");
        return;
    }

    if (range && typeof range === 'string' && formula.iscelldata(range)) {
        range = formula.getcellrange(range);
    }

    let r1 = range.row[0],
        r2 = range.row[1],
        c1 = range.column[0],
        c2 = range.column[1];

    let str;
    if (hasTitle) {
        str = r1 + 1;
    } else {
        str = r1;
    }

    let hasMc = false; // Whether the sort selection has merged cells
    let data = [];
    for (let r = str; r <= r2; r++) {
        let data_row = [];
        for (let c = c1; c <= c2; c++) {
            if (fileData[r][c] != null && fileData[r][c].mc != null) {
                hasMc = true;
                break;
            }
            data_row.push(fileData[r][c]);
        }
        data.push(data_row);
    }

    if (hasMc) {
        Store.message && Store.message.info(locale().sort.mergeError);
        return;
    }

    sort.forEach(sortItem => {
        let i = sortItem.i;
        i -= c1;
        data = orderbydata([].concat(data), i, sortItem.sort === 'asc');
    });

    for (let r = str; r <= r2; r++) {
        for (let c = c1; c <= c2; c++) {
            fileData[r][c] = data[r - str][c - c1];
        }
    }

    let allParam = {};
    if (cfg.rowlen != null) {
        cfg = rowlenByRange(fileData, str, r2, cfg);

        allParam = {
            "cfg": cfg,
            "RowlChange": true
        };
    }

    if (file.index === Store.currentSheetIndex) {
        jfrefreshgrid(fileData, [{ "row": [str, r2], "column": [c1, c2] }], allParam);
    } else {
        file.data = fileData;
        file.config = cfg;
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 *  为指定索引的工作表，选定的范围开启条件格式，根据设置的条件格式规则突出显示部分单元格，返回开启条件格式后的数据。
 * @param {String} conditionName 条件格式规则类型
 * @param {Object} conditionValue 可以设置条件单元格或者条件值
 * @param {Object} options 可选参数
 * @param {Object} options.format 颜色设置
 * @param {Array | Object | String} options.cellrange 选区范围
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setRangeConditionalFormatDefault (conditionName, conditionValue, options = {}) {
    let conditionNameValues = [
        'greaterThan',
        'lessThan',
        'betweenness',
        'equal',
        'textContains',
        'occurrenceDate',
        'duplicateValue',
        'top10',
        'top10%',
        'last10',
        'last10%',
        'AboveAverage',
        'SubAverage'
    ];

    if (!conditionName || !conditionNameValues.includes(conditionName)) {
        return tooltip.info('The conditionName parameter is invalid.', '');
    }

    if (getObjType(conditionValue) != 'array' || conditionValue.length == 0) {
        return tooltip.info('The conditionValue parameter is invalid.', '');
    }

    let {
        format = {
            "textColor": "#000000",
            "cellColor": "#ff0000"
        },
        cellrange = Store.luckysheet_select_save,
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    cellrange = JSON.parse(JSON.stringify(cellrange));

    let file = Store.luckysheetfile[order];
    let data = file.data;

    if (data == null || data.length == 0) {
        data = sheetmanage.buildGridData(file);
    }

    if (file == null) {
        return tooltip.info('Incorrect worksheet index', '');
    }

    const conditionformat_Text = locale().conditionformat;

    let conditionRange = [], conditionValue2 = [];

    if (conditionName == 'betweenness') {
        let v1 = conditionValue[0];
        let v2 = conditionValue[1];

        // 条件值是否是选区
        let rangeArr1 = conditionformat.getRangeByTxt(v1);
        if (rangeArr1.length > 1) {
            conditionformat.infoDialog(conditionformat_Text.onlySingleCell, "");
            return;
        } else if (rangeArr1.length == 1) {
            let r1 = rangeArr1[0].row[0], r2 = rangeArr1[0].row[1];
            let c1 = rangeArr1[0].column[0], c2 = rangeArr1[0].column[1];

            if (r1 == r2 && c1 == c2) {
                v1 = getcellvalue(r1, c1, data);

                conditionRange.push({ "row": rangeArr1[0].row, "column": rangeArr1[0].column });
                conditionValue2.push(v1);
            } else {
                conditionformat.infoDialog(conditionformat_Text.onlySingleCell, "");
                return;
            }
        } else if (rangeArr1.length == 0) {
            if (isNaN(v1) || v1 == "") {
                conditionformat.infoDialog(conditionformat_Text.conditionValueCanOnly, "");
                return;
            } else {
                conditionValue2.push(v1);
            }
        }

        let rangeArr2 = conditionformat.getRangeByTxt(v2);
        if (rangeArr2.length > 1) {
            conditionformat.infoDialog(conditionformat_Text.onlySingleCell, "");
            return;
        } else if (rangeArr2.length == 1) {
            let r1 = rangeArr2[0].row[0], r2 = rangeArr2[0].row[1];
            let c1 = rangeArr2[0].column[0], c2 = rangeArr2[0].column[1];

            if (r1 == r2 && c1 == c2) {
                v2 = getcellvalue(r1, c1, data);

                conditionRange.push({ "row": rangeArr2[0].row, "column": rangeArr2[0].column });
                conditionValue2.push(v2);
            } else {
                conditionformat.infoDialog(conditionformat_Text.onlySingleCell, "");
                return;
            }
        } else if (rangeArr2.length == 0) {
            if (isNaN(v2) || v2 == "") {
                conditionformat.infoDialog(conditionformat_Text.conditionValueCanOnly, "");
                return;
            } else {
                conditionValue2.push(v2);
            }
        }
    } else if (conditionName == 'greaterThan' || conditionName == 'lessThan' || conditionName == 'equal') {
        let v = conditionValue[0];

        // 条件值是否是选区
        let rangeArr = conditionformat.getRangeByTxt(v);
        if (rangeArr.length > 1) {
            conditionformat.infoDialog(conditionformat_Text.onlySingleCell, "");
            return;
        } else if (rangeArr.length == 1) {
            let r1 = rangeArr[0].row[0], r2 = rangeArr[0].row[1];
            let c1 = rangeArr[0].column[0], c2 = rangeArr[0].column[1];

            if (r1 == r2 && c1 == c2) {
                v = getcellvalue(r1, c1, data);

                conditionRange.push({ "row": rangeArr[0].row, "column": rangeArr[0].column });
                conditionValue2.push(v);
            } else {
                conditionformat.infoDialog(conditionformat_Text.onlySingleCell, "");
                return;
            }
        } else if (rangeArr.length == 0) {
            if (isNaN(v) || v == "") {
                conditionformat.infoDialog(conditionformat_Text.conditionValueCanOnly, "");
                return;
            } else {
                conditionValue2.push(v);
            }
        }
    } else if (conditionName == 'textContains') {
        let v = conditionValue[0];

        // 条件值是否是选区
        let rangeArr = conditionformat.getRangeByTxt(v);
        if (rangeArr.length > 1) {
            conditionformat.infoDialog(conditionformat_Text.onlySingleCell, "");
            return;
        } else if (rangeArr.length == 1) {
            let r1 = rangeArr[0].row[0], r2 = rangeArr[0].row[1];
            let c1 = rangeArr[0].column[0], c2 = rangeArr[0].column[1];

            if (r1 == r2 && c1 == c2) {
                v = getcellvalue(r1, c1, data);

                conditionRange.push({ "row": rangeArr[0].row, "column": rangeArr[0].column });
                conditionValue2.push(v);
            } else {
                conditionformat.infoDialog(conditionformat_Text.onlySingleCell, "");
                return;
            }
        } else if (rangeArr.length == 0) {
            if (v == "") {
                conditionformat.infoDialog(conditionformat_Text.conditionValueCanOnly, "");
                return;
            } else {
                conditionValue2.push(v);
            }
        }
    } else if (conditionName == 'occurrenceDate') {
        let v1 = conditionValue[0];
        let v2 = conditionValue[1];

        if (!isdatetime(v1) || !isdatetime(v2)) {
            return tooltip.info('The conditionValue parameter is invalid.', '');
        }

        let v;
        if (diff(v1, v2) > 0) {
            v = dayjs(v2).format("YYYY/MM/DD") + "-" + dayjs(v1).format("YYYY/MM/DD");
        } else {
            v = dayjs(v1).format("YYYY/MM/DD") + "-" + dayjs(v2).format("YYYY/MM/DD");
        }

        conditionValue2.push(v);
    } else if (conditionName == 'duplicateValue') {
        let v = conditionValue[0];

        if (v != '0' || v != '1') {
            return tooltip.info('The conditionValue parameter is invalid.', '');
        }

        conditionValue2.push(v);
    } else if (conditionName == 'top10' || conditionName == 'top10%' || conditionName == 'last10' || conditionName == 'last10%') {
        let v = conditionValue[0];

        if (parseInt(v) != v || parseInt(v) < 1 || parseInt(v) > 1000) {
            conditionformat.infoDialog(conditionformat_Text.pleaseEnterInteger, "");
            return;
        }

        conditionValue2.push(parseInt(v));
    } else if (conditionName == 'AboveAverage' || conditionName == 'SubAverage') {
        conditionValue2.push(conditionName);
    }

    if (!format.hasOwnProperty("textColor") || !format.hasOwnProperty("cellColor")) {
        return tooltip.info('The format parameter is invalid.', '');
    }

    if (getObjType(cellrange) == 'string') {
        cellrange = conditionformat.getRangeByTxt(cellrange);
    } else if (getObjType(cellrange) == 'object') {
        cellrange = [cellrange];
    }

    if (getObjType(cellrange) != 'array') {
        return tooltip.info('The cellrange parameter is invalid.', '');
    }

    let rule = {
        "type": "default",
        "cellrange": cellrange,
        "format": format,
        "conditionName": conditionName,
        "conditionRange": conditionRange,
        "conditionValue": conditionValue2
    };

    // 保存之前的规则
    let fileH = $.extend(true, [], Store.luckysheetfile);
    let historyRules = conditionformat.getHistoryRules(fileH);

    // 保存当前的规则
    let ruleArr = file.luckysheet_conditionformat_save || [];
    ruleArr.push(rule);
    file.luckysheet_conditionformat_save = ruleArr;

    let fileC = $.extend(true, [], Store.luckysheetfile);
    let currentRules = conditionformat.getCurrentRules(fileC);

    // 刷新一次表格
    conditionformat.ref(historyRules, currentRules);

    // 发送给后台
    if (server.allowUpdate) {
        server.saveParam("all", file.index, ruleArr, { "k": "luckysheet_conditionformat_save" });
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 为指定索引的工作表，选定的范围开启条件格式，返回开启条件格式后的数据。
 * @param {String} type 条件格式规则类型
 * @param {Object} options 可选参数
 * @param {Array | String} options.format 颜色设置
 * @param {Array | Object | String} options.cellrange 选区范围
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setRangeConditionalFormat (type, options = {}) {
    let typeValues = [
        'dataBar',
        'colorGradation',
        'icons'
    ];

    if (!type || !typeValues.includes(type)) {
        return tooltip.info('The type parameter is invalid.', '');
    }

    let {
        format,
        cellrange = Store.luckysheet_select_save,
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    cellrange = JSON.parse(JSON.stringify(cellrange));
    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info('Incorrect worksheet index', '');
    }

    if (type == 'dataBar') {
        if (format == null) {
            format = ["#638ec6", "#ffffff"];
        }

        if (getObjType(format) != 'array' || format.length < 1 || format.length > 2) {
            return tooltip.info('The format parameter is invalid.', '');
        }
    } else if (type == 'colorGradation') {
        if (format == null) {
            format = ["rgb(99, 190, 123)", "rgb(255, 235, 132)", "rgb(248, 105, 107)"];
        }

        if (getObjType(format) != 'array' || format.length < 2 || format.length > 3) {
            return tooltip.info('The format parameter is invalid.', '');
        }
    } else if (type == 'icons') {
        if (format == null) {
            format = "threeWayArrowMultiColor";
        }

        let formatValues = [
            'threeWayArrowMultiColor',
            'threeTriangles',
            'fourWayArrowMultiColor',
            'fiveWayArrowMultiColor',
            'threeWayArrowGrayColor',
            'fourWayArrowGrayColor',
            'fiveWayArrowGrayColor',
            'threeColorTrafficLightRimless',
            'threeSigns',
            'greenRedBlackGradient',
            'threeColorTrafficLightBordered',
            'fourColorTrafficLight',
            'threeSymbolsCircled',
            'tricolorFlag',
            'threeSymbolsnoCircle',
            'threeStars',
            'fiveQuadrantDiagram',
            'fiveBoxes',
            'grade4',
            'grade5'
        ];

        if (getObjType(format) != 'string' || !formatValues.includes(format)) {
            return tooltip.info('The format parameter is invalid.', '');
        }

        switch (format) {
        case 'threeWayArrowMultiColor':
            format = {
                "len": 3,
                "leftMin": 0,
                "top": 0
            };
            break;
        case 'threeTriangles':
            format = {
                "len": 3,
                "leftMin": 0,
                "top": 1
            };
            break;
        case 'fourWayArrowMultiColor':
            format = {
                "len": 4,
                "leftMin": 0,
                "top": 2
            };
            break;
        case 'fiveWayArrowMultiColor':
            format = {
                "len": 5,
                "leftMin": 0,
                "top": 3
            };
            break;
        case 'threeWayArrowGrayColor':
            format = {
                "len": 3,
                "leftMin": 5,
                "top": 0
            };
            break;
        case 'fourWayArrowGrayColor':
            format = {
                "len": 4,
                "leftMin": 5,
                "top": 1
            };
            break;
        case 'fiveWayArrowGrayColor':
            format = {
                "len": 5,
                "leftMin": 5,
                "top": 2
            };
            break;
        case 'threeColorTrafficLightRimless':
            format = {
                "len": 3,
                "leftMin": 0,
                "top": 4
            };
            break;
        case 'threeSigns':
            format = {
                "len": 3,
                "leftMin": 0,
                "top": 5
            };
            break;
        case 'greenRedBlackGradient':
            format = {
                "len": 4,
                "leftMin": 0,
                "top": 6
            };
            break;
        case 'threeColorTrafficLightBordered':
            format = {
                "len": 3,
                "leftMin": 5,
                "top": 4
            };
            break;
        case 'fourColorTrafficLight':
            format = {
                "len": 4,
                "leftMin": 5,
                "top": 5
            };
            break;
        case 'threeSymbolsCircled':
            format = {
                "len": 3,
                "leftMin": 0,
                "top": 7
            };
            break;
        case 'tricolorFlag':
            format = {
                "len": 3,
                "leftMin": 0,
                "top": 8
            };
            break;
        case 'threeSymbolsnoCircle':
            format = {
                "len": 3,
                "leftMin": 5,
                "top": 7
            };
            break;
        case 'threeStars':
            format = {
                "len": 3,
                "leftMin": 0,
                "top": 9
            };
            break;
        case 'fiveQuadrantDiagram':
            format = {
                "len": 5,
                "leftMin": 0,
                "top": 10
            };
            break;
        case 'fiveBoxes':
            format = {
                "len": 5,
                "leftMin": 0,
                "top": 11
            };
            break;
        case 'grade4':
            format = {
                "len": 4,
                "leftMin": 5,
                "top": 9
            };
            break;
        case 'grade5':
            format = {
                "len": 5,
                "leftMin": 5,
                "top": 10
            };
            break;
        }
    }

    if (getObjType(cellrange) == 'string') {
        cellrange = conditionformat.getRangeByTxt(cellrange);
    } else if (getObjType(cellrange) == 'object') {
        cellrange = [cellrange];
    }

    if (getObjType(cellrange) != 'array') {
        return tooltip.info('The cellrange parameter is invalid.', '');
    }

    let rule = {
        "type": type,
        "cellrange": cellrange,
        "format": format
    };

    // 保存之前的规则
    let fileH = $.extend(true, [], Store.luckysheetfile);
    let historyRules = conditionformat.getHistoryRules(fileH);

    // 保存当前的规则
    let ruleArr = file.luckysheet_conditionformat_save || [];
    ruleArr.push(rule);
    file.luckysheet_conditionformat_save = ruleArr;

    let fileC = $.extend(true, [], Store.luckysheetfile);
    let currentRules = conditionformat.getCurrentRules(fileC);

    // 刷新一次表格
    conditionformat.ref(historyRules, currentRules);

    // 发送给后台
    if (server.allowUpdate) {
        server.saveParam("all", file.index, ruleArr, { "k": "luckysheet_conditionformat_save" });
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 为指定下标的工作表，删除条件格式规则，返回被删除的条件格式规则
 * @param {Number} itemIndex 条件格式规则索引
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteRangeConditionalFormat (itemIndex, options = {}) {
    if (!isRealNum(itemIndex)) {
        return tooltip.info('The itemIndex parameter is invalid.', '');
    }

    itemIndex = Number(itemIndex);

    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info('The order parameter is invalid.', '');
    }

    let cdformat = $.extend(true, [], file.luckysheet_conditionformat_save);

    if (cdformat.length == 0) {
        return tooltip.info('This worksheet has no conditional format to delete', '');
    } else if (cdformat[itemIndex] == null) {
        return tooltip.info('The conditional format of the index cannot be found', '');
    }

    let cdformatItem = cdformat.splice(itemIndex, 1);

    // 保存之前的规则
    let fileH = $.extend(true, [], Store.luckysheetfile);
    let historyRules = conditionformat.getHistoryRules(fileH);

    // 保存当前的规则
    file.luckysheet_conditionformat_save = cdformat;

    let fileC = $.extend(true, [], Store.luckysheetfile);
    let currentRules = conditionformat.getCurrentRules(fileC);

    // 刷新一次表格
    conditionformat.ref(historyRules, currentRules);

    // 发送给后台
    if (server.allowUpdate) {
        server.saveParam("all", file.index, ruleArr, { "k": "luckysheet_conditionformat_save" });
    }

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    return cdformatItem;
}

/**
 * 清除指定工作表指定单元格区域的内容，不同于删除选区的功能，不需要设定单元格移动情况
 * @param {Object} options 可选参数
 * @param {Array | Object | String} options.range 要清除的选区范围
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function clearRange (options = {}) {
    let {
        range = Store.luckysheet_select_save,
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    range = JSON.parse(JSON.stringify(range));
    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        let cellrange = formula.getcellrange(range);
        range = [{
            "row": cellrange.row,
            "column": cellrange.column
        }];
    } else if (getObjType(range) == 'object') {
        if (range.row == null || range.column == null) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        range = [{
            "row": range.row,
            "column": range.column
        }];
    }

    if (getObjType(range) != 'array') {
        return tooltip.info("The range parameter is invalid.", "");
    }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let cfg = $.extend(true, {}, file.config);
    let has_PartMC = false;

    for (let s = 0; s < range.length; s++) {
        let r1 = range[s].row[0],
            r2 = range[s].row[1];
        let c1 = range[s].column[0],
            c2 = range[s].column[1];

        has_PartMC = hasPartMC(cfg, r1, r2, c1, c2);

        if (has_PartMC) {
            break;
        }
    }

    if (has_PartMC) {
        return tooltip.info('Cannot perform this operation on partially merged cells', '');
    }

    let d = $.extend(true, [], file.data);

    if (d.length == 0) {
        d = $.extend(true, [], sheetmanage.buildGridData(file));
    }

    for (let s = 0; s < range.length; s++) {
        let r1 = range[s].row[0],
            r2 = range[s].row[1];
        let c1 = range[s].column[0],
            c2 = range[s].column[1];

        for (let r = r1; r <= r2; r++) {
            for (let c = c1; c <= c2; c++) {
                let cell = d[r][c];

                if (getObjType(cell) == "object") {
                    delete cell.m;
                    delete cell.v;

                    if (cell.f != null) {
                        delete cell.f;
                        formula.delFunctionGroup(r, c, file.index);

                        delete cell.spl;
                    }

                    if (cell.ct != null && cell.ct.t == 'inlineStr') {
                        delete cell.ct;
                    }
                } else {
                    d[r][c] = null;
                }
            }
        }
    }

    if (file.index == Store.currentSheetIndex) {
        jfrefreshgrid(d, range);
    } else {
        file.data = d;
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 删除指定工作表指定单元格区域，返回删除掉的数据，同时，指定是右侧单元格左移还是下方单元格上移
 * @param {String} move 删除后，单元格左移/上移
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 要删除的选区范围
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteRange (move, options = {}) {
    let moveList = ['left', 'up'];

    if (!moveList.includes(move)) {
        return tooltip.info("The move parameter is invalid.", "");
    }

    let {
        range = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1],
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        let cellrange = formula.getcellrange(range);
        range = {
            "row": cellrange.row,
            "column": cellrange.column
        };
    }

    if (getObjType(range) != 'object' || range.row == null || range.column == null) {
        return tooltip.info("The range parameter is invalid.", "");
    }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let str = range.row[0],
        edr = range.row[1],
        stc = range.column[0],
        edc = range.column[1];

    if (move == 'left') {
        luckysheetDeleteCell('moveLeft', str, edr, stc, edc, order);
    } else if (move == 'up') {
        luckysheetDeleteCell('moveUp', str, edr, stc, edc, order);
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 指定工作表指定单元格区域的数据进行矩阵操作，返回操作成功后的结果数据
 * @param {String} type 矩阵操作的类型
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Function} options.success 操作结束的回调函数
 */
export function matrixOperation (type, options = {}) {
    let typeValues = [
        'flipUpDown',               // 上下翻转
        'flipLeftRight',            // 左右翻转
        'flipClockwise',            // 顺时针旋转
        'flipCounterClockwise',     // 逆时针旋转
        'transpose',                // 转置
        'deleteZeroByRow',          // 按行删除两端0值
        'deleteZeroByColumn',       // 按列删除两端0值
        'removeDuplicateByRow',     // 按行删除重复值
        'removeDuplicateByColumn',  // 按列删除重复值
        'newMatrix'                 // 生产新矩阵
    ];

    if (!type || typeValues.indexOf(type) < 0) {
        return tooltip.info('The type parameter is invalid.', '');
    }

    let curRange = Store.luckysheet_select_save[0];
    let {
        range = curRange,
        success
    } = { ...options };

    if (range instanceof Array && range.length > 1) {
        tooltip.info(locale().drag.noMulti, "");
        return;
    }

    if (range && typeof range === 'string' && formula.iscelldata(range)) {
        range = formula.getcellrange(range);
    }

    let getdata = getdatabyselection(range);
    let arr = [];
    if (getdata.length === 0) {
        return;
    }

    let getdatalen, collen, arr1;
    switch (type) {
    case 'flipUpDown':
        for (let r = getdata.length - 1; r >= 0; r--) {
            let a = [];
            for (let c = 0; c < getdata[0].length; c++) {
                let value = "";
                if (getdata[r] != null && getdata[r][c] != null) {
                    value = getdata[r][c];
                }
                a.push(value);
            }
            arr.push(a);
        }
        break;
    case 'flipLeftRight':
        for (let r = 0; r < getdata.length; r++) {
            let a = [];
            for (let c = getdata[0].length - 1; c >= 0; c--) {
                let value = "";
                if (getdata[r] != null && getdata[r][c] != null) {
                    value = getdata[r][c];
                }
                a.push(value);
            }
            arr.push(a);
        }
        break;
    case 'flipClockwise':
        for (let c = 0; c < getdata[0].length; c++) {
            let a = [];
            for (let r = getdata.length - 1; r >= 0; r--) {
                let value = "";
                if (getdata[r] != null && getdata[r][c] != null) {
                    value = getdata[r][c];
                }
                a.push(value);
            }
            arr.push(a);
        }
        break;
    case 'flipCounterClockwise':
        for (let c = getdata[0].length - 1; c >= 0; c--) {
            let a = [];
            for (let r = 0; r < getdata.length; r++) {
                let value = "";
                if (getdata[r] != null && getdata[r][c] != null) {
                    value = getdata[r][c];
                }
                a.push(value);
            }
            arr.push(a);
        }
        break;
    case 'transpose':
        for (let c = 0; c < getdata[0].length; c++) {
            let a = [];
            for (let r = 0; r < getdata.length; r++) {
                let value = "";
                if (getdata[r] != null && getdata[r][c] != null) {
                    value = getdata[r][c];
                }
                a.push(value);
            }
            arr.push(a);
        }
        break;
    case 'deleteZeroByRow':
        getdatalen = getdata[0].length;
        for (let r = 0; r < getdata.length; r++) {
            let a = [], stdel = true, eddel = true;
            for (let c = 0; c < getdatalen; c++) {
                let value = "";
                if (getdata[r] != null && getdata[r][c] != null) {
                    value = getdata[r][c];
                    if ((value.v == "0" || value.v == 0) && stdel) {
                        continue;
                    } else {
                        stdel = false;
                    }
                }
                a.push(value);
            }

            let a1 = [];
            if (a.length == getdatalen) {
                a1 = a;
            } else {
                for (let c = a.length - 1; c >= 0; c--) {
                    let value = "";
                    if (a[c] != null) {
                        value = a[c];
                        if ((value.v == "0" || value.v == 0) && eddel) {
                            continue;
                        } else {
                            eddel = false;
                        }
                    }
                    a1.unshift(value);
                }

                let l = getdatalen - a1.length;
                for (let c1 = 0; c1 < l; c1++) {
                    a1.push("");
                }
            }
            arr.push(a1);
        }
        break;
    case 'deleteZeroByColumn':
        getdatalen = getdata.length;
        collen = getdata[0].length;
        for (let c = 0; c < collen; c++) {
            let a = [], stdel = true, eddel = true;
            for (let r = 0; r < getdatalen; r++) {
                let value = "";
                if (getdata[r] != null && getdata[r][c] != null) {
                    value = getdata[r][c];
                    if ((value.v == "0" || value.v == 0) && stdel) {
                        continue;
                    } else {
                        stdel = false;
                    }
                }
                a.push(value);
            }

            let a1 = [];
            if (a.length == getdatalen) {
                a1 = a;
            } else {
                for (let r = a.length - 1; r >= 0; r--) {
                    let value = "";
                    if (a[r] != null) {
                        value = a[r];
                        if ((value.v == "0" || value.v == 0) && eddel) {
                            continue;
                        } else {
                            eddel = false;
                        }
                    }
                    a1.unshift(value);
                }

                let l = getdatalen - a1.length;
                for (let r1 = 0; r1 < l; r1++) {
                    a1.push("");
                }
            }
            arr.push(a1);
        }

        arr1 = [];
        for (let c = 0; c < arr[0].length; c++) {
            let a = [];
            for (let r = 0; r < arr.length; r++) {
                let value = "";
                if (arr[r] != null && arr[r][c] != null) {
                    value = arr[r][c];
                }
                a.push(value);
            }
            arr1.push(a);
        }
        break;
    case 'removeDuplicateByRow':
        getdatalen = getdata[0].length;
        for (let r = 0; r < getdata.length; r++) {
            let a = [], repeat = {};

            for (let c = 0; c < getdatalen; c++) {
                let value = null;
                if (getdata[r] != null && getdata[r][c] != null) {
                    value = getdata[r][c];

                    if (value.v in repeat) {
                        repeat[value.v].push(value);
                    } else {
                        repeat[value.v] = [];
                        repeat[value.v].push(value);
                    }
                }
            }

            for (let c = 0; c < getdatalen; c++) {
                let value = null;
                if (getdata[r] != null && getdata[r][c] != null) {
                    value = getdata[r][c];

                    if (repeat[value.v].length == 1) {
                        a.push(value);
                    }
                }
            }

            let l = getdatalen - a.length;
            for (let c1 = 0; c1 < l; c1++) {
                a.push(null);
            }
            arr.push(a);
        }
        break;
    case 'removeDuplicateByColumn':
        collen = getdata[0].length;
        getdatalen = getdata.length;
        for (let c = 0; c < collen; c++) {
            let a = [], repeat = {};

            for (let r = 0; r < getdatalen; r++) {
                let value = null;
                if (getdata[r] != null && getdata[r][c] != null) {
                    value = getdata[r][c];

                    if (value.v in repeat) {
                        repeat[value.v].push(value);
                    } else {
                        repeat[value.v] = [];
                        repeat[value.v].push(value);
                    }
                }
            }

            for (let r = 0; r < getdatalen; r++) {
                let value = null;
                if (getdata[r] != null && getdata[r][c] != null) {
                    value = getdata[r][c];

                    if (repeat[value.v].length == 1) {
                        a.push(value);
                    }
                }
            }

            let a1 = a;
            let l = getdatalen - a1.length;
            for (let r1 = 0; r1 < l; r1++) {
                a1.push(null);
            }
            arr.push(a1);
        }

        arr1 = [];
        for (let c = 0; c < arr[0].length; c++) {
            let a = [];
            for (let r = 0; r < arr.length; r++) {
                let value = null;
                if (arr[r] != null && arr[r][c] != null) {
                    value = arr[r][c];
                }
                a.push(value);
            }
            arr1.push(a);
        }
        break;
    case 'newMatrix':
        // TODO
        console.log("TODO");
        break;
    }
    editor.controlHandler(arr, range);

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 指定工作表指定单元格区域的数据进行矩阵计算，返回计算成功后的结果数据
 * @param {String} type 计算方式
 * @param {Number} number 计算数值
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Function} options.success 操作结束的回调函数
 */
export function matrixCalculation (type, number, options = {}) {
    let typeValues = [
        'plus',     // 加
        'minus',    // 减
        'multiply', // 乘
        'divided',  // 除
        'power',    // 幂
        'root',     // 次方根
        'log'       // 对数log
    ];

    if (!type || typeValues.indexOf(type) < 0) {
        return tooltip.info('The type parameter is invalid.', '');
    }

    if (!isRealNum(number)) {
        return tooltip.info('The number parameter is invalid.', '');
    }

    let curRange = Store.luckysheet_select_save[0];
    let {
        range = curRange,
        success
    } = { ...options };

    if (range instanceof Array && range.length > 1) {
        tooltip.info(locale().drag.noMulti, "");
        return;
    }

    if (range && typeof range === 'string' && formula.iscelldata(range)) {
        range = formula.getcellrange(range);
    }

    let getdata = getdatabyselection(range);
    if (getdata.length == 0) {
        return;
    }

    let arr = [];
    for (let r = 0; r < getdata.length; r++) {
        let a = [];
        for (let c = 0; c < getdata[0].length; c++) {
            let value = "";
            if (getdata[r] != null && getdata[r][c] != null) {
                value = getdata[r][c];
                if (parseInt(value) != null && getdata[r][c].ct != undefined && getdata[r][c].ct.t == "n") {
                    if (type == "minus") {
                        value.v = value.v - number;
                    } else if (type == "multiply") {
                        value.v = value.v * number;
                    } else if (type == "divided") {
                        value.v = numFormat(value.v / number, 4);
                    } else if (type == "power") {
                        value.v = Math.pow(value.v, number);
                    } else if (type == "root") {
                        if (number == 2) {
                            value.v = numFormat(Math.sqrt(value.v), 4);
                        } else if (number == 3 && Math.cbrt) {
                            value.v = numFormat(Math.cbrt(value.v), 4);
                        } else {
                            value.v = numFormat(jfnqrt(value.v, number), 4);
                        }
                    } else if (type == "log") {
                        value.v = numFormat(Math.log(value.v) * 10000 / Math.log(Math.abs(number)), 4);
                    } else {
                        value.v = value.v + number;
                    }

                    if (value.v == null) {
                        value.m = "";
                    } else {
                        value.m = value.v.toString();
                    }
                }
            }
            a.push(value);
        }
        arr.push(a);
    }

    editor.controlHandler(arr, range);

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 新增一个sheet，返回新增的工作表对象
 * @param {Object} options 可选参数
 * @param {Object} options.sheetObject 新增的工作表的数据；默认值为空对象
 * @param {Number} options.order 新增的工作表索引；默认值为最后一个索引位置
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetAdd (options = {}) {
    let lastOrder = Store.luckysheetfile.length - 1;
    let {
        sheetObject = {},
        order = lastOrder,
        success
    } = { ...options };

    if (!isRealNum(order)) {
        return tooltip.info("Parameter is not a table index", "");
    }

    order = Number(order);

    let index = sheetmanage.generateRandomSheetIndex();
    if (sheetObject.celldata) {
        sheetObject.celldata = sheetObject.celldata.map((cell) => {
            if (cell && cell.v && cell.v.v === '#d') {
                return {
                    ...cell,
                    v: {
                        ...cell.v,
                        v: '#待查询',
                        m: '#待查询'
                    }
                };
            }
            return cell;
        });
    }
    // calcChain公式链里的index也要跟着变化
    sheetObject.calcChain = sheetObject.calcChain || [];
    sheetObject.calcChain = sheetObject.calcChain.length || sheetObject.celldata.filter(c => c && c.v && c.v.f).map((cell) => {
        return {
            r: cell.r,
            c: cell.c,
            index
        };
    });
    Store.conentCoordinate[index] = {
        start: [],
        end: []
    };
    let sheetname = sheetmanage.generateRandomSheetName(Store.luckysheetfile, false);
    if (!!sheetObject.name) {
        let sameName = false;

        for (let i = 0; i < Store.luckysheetfile.length; i++) {
            if (Store.luckysheetfile[i].name == sheetObject.name) {
                sameName = true;
                break;
            }
        }

        if (!sameName) {
            sheetname = sheetObject.name;
        }
    }

    $("#luckysheet-sheet-container-c").append(replaceHtml(sheetHTML, {
        "index": index,
        "active": "",
        "name": sheetname,
        "style": "",
        "colorset": ""
    }));

    let sheetconfig = {
        "name": "",
        "color": "",
        "status": "0",
        "order": "",
        "index": "",
        "celldata": [],
        "row": Store.defaultrowNum,
        "column": Store.defaultcolumnNum,
        "config": {},
        "pivotTable": null,
        "isPivotTable": false
    };
    sheetconfig = $.extend(true, sheetconfig, sheetObject);

    sheetconfig.index = index;
    sheetconfig.name = sheetname;
    sheetconfig.order = order;

    if (order <= 0) {
        let beforeIndex = Store.luckysheetfile[0].index;
        let beforeObj = $("#luckysheet-sheets-item" + beforeIndex);
        $("#luckysheet-sheets-item" + index).insertBefore(beforeObj);

        Store.luckysheetfile.splice(0, 0, sheetconfig);
    } else {
        if (order > Store.luckysheetfile.length) {
            order = Store.luckysheetfile.length;
        }

        let afterIndex = Store.luckysheetfile[order - 1].index;
        let afterObj = $("#luckysheet-sheets-item" + afterIndex);
        $("#luckysheet-sheets-item" + index).insertAfter(afterObj);

        Store.luckysheetfile.splice(order, 0, sheetconfig);
    }

    let orders = {};

    Store.luckysheetfile.forEach((item, i, arr) => {
        arr[i].order = i;
        orders[item.index.toString()] = i;
    });

    $("#luckysheet-sheet-area div.luckysheet-sheets-item").removeClass("luckysheet-sheets-item-active");
    $("#luckysheet-sheets-item" + index).addClass("luckysheet-sheets-item-active");
    $("#luckysheet-cell-main").append('<div id="luckysheet-datavisual-selection-set-' + index + '" class="luckysheet-datavisual-selection-set"></div>');
    cleargridelement(true);

    server.saveParam("sha", null, $.extend(true, {}, sheetconfig));
    server.saveParam("shr", null, orders);

    if (Store.clearjfundo) {
        Store.jfundo.length  = 0;
        let redo = {};
        redo.type = "addSheet";
        redo.sheetconfig = $.extend(true, {}, sheetconfig);
        redo.index = index;
        redo.currentSheetIndex = Store.currentSheetIndex;
        Store.jfredo.push(redo);
    }

    sheetmanage.changeSheetExec(index, false, true);

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 删除指定下标的工作表，返回已删除的工作表对象
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetDelete (options = {}) {
    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    if (Store.luckysheetfile.length === 1) {
        return tooltip.info(locale().sheetconfig.noMoreSheet, "");
    }

    sheetmanage.deleteSheet(file.index);

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    return file;
}

/**
 * 复制指定下标的工作表到指定下标位置
 * @param {Object} options 可选参数
 * @param {Number} options.targetOrder 新复制的工作表目标下标位置；默认值为当前工作表下标的下一个下标位置（递增）
 * @param {Number} options.order 被复制的工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetCopy (options = {}) {
    let {
        targetOrder,
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    if (targetOrder == null) {
        targetOrder = order + 1;
    }

    if (!isRealNum(targetOrder)) {
        return tooltip.info("The targetOrder parameter is invalid.", "");
    }

    let copyindex = file.index;
    let index = sheetmanage.generateRandomSheetIndex();

    let copyjson = $.extend(true, {}, file);
    copyjson.order = Store.luckysheetfile.length;
    copyjson.index = index;
    copyjson.name = sheetmanage.generateCopySheetName(Store.luckysheetfile, copyjson.name);

    let colorset = '';
    if (copyjson.color != null) {
        colorset = '<div class="luckysheet-sheets-item-color" style=" position: absolute; width: 100%; height: 3px; bottom: 2px; left: 0px; background-color: ' + copyjson.color || 'var(--ux-primary-color, #555FFF)' + ';"></div>';
    }

    let afterObj = $("#luckysheet-sheets-item" + copyindex);
    if (isRealNum(targetOrder)) {
        afterObj = $("#luckysheet-sheets-item" + Store.luckysheetfile[targetOrder - 1].index);
    }

    $("#luckysheet-sheet-container-c").append(replaceHtml(sheetHTML, {
        "index": copyjson.index,
        "active": "",
        "name": copyjson.name,
        "order": copyjson.order,
        "style": "",
        "colorset": colorset
    }));
    $("#luckysheet-sheets-item" + copyjson.index).insertAfter(afterObj);
    Store.luckysheetfile.splice(targetOrder, 0, copyjson);

    $("#luckysheet-sheet-area div.luckysheet-sheets-item").removeClass("luckysheet-sheets-item-active");
    $("#luckysheet-sheets-item" + index).addClass("luckysheet-sheets-item-active");
    $("#luckysheet-cell-main").append('<div id="luckysheet-datavisual-selection-set-' + index + '" class="luckysheet-datavisual-selection-set"></div>');
    cleargridelement(true);

    server.saveParam("shc", index, { "copyindex": copyindex, "name": copyjson.name });

    sheetmanage.changeSheetExec(index);
    sheetmanage.reOrderAllSheet();

    if (Store.clearjfundo) {
        Store.jfredo.push({
            "type": "copySheet",
            "copyindex": copyindex,
            "index": copyjson.index,
            "sheetIndex": copyjson.index
        });
    } else if (Store.jfredo.length > 0) {
        let jfredostr = Store.jfredo[Store.jfredo.length - 1];

        if (jfredostr.type == "copySheet") {
            jfredostr.index = copyjson.index;
            jfredostr.sheetIndex = copyjson.index;
        }
    }

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    return copyjson;
}

/**
 * 隐藏指定下标的工作表，返回被隐藏的工作表对象
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetHide (options = {}) {
    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    sheetmanage.setSheetHide(file.index);

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    return file;
}

/**
 * 取消隐藏指定下标的工作表，返回被取消隐藏的工作表对象
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetShow (options = {}) {
    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    sheetmanage.setSheetShow(file.index);

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    return file;
}

/**
 * 设置指定下标的工作表为当前工作表（激活态），即切换到指定的工作表，返回被激活的工作表对象
 * @param {Number} order 要激活的工作表下标
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetActive (order, options = {}) {
    if (order == null || !isRealNum(order) || Store.luckysheetfile[order] == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let file = Store.luckysheetfile[order];

    let {
        success
    } = { ...options };
    $("#luckysheet-sheet-area div.luckysheet-sheets-item").removeClass("luckysheet-sheets-item-active");
    $("#luckysheet-sheets-item" + file.index).addClass("luckysheet-sheets-item-active");

    sheetmanage.changeSheet(file.index, undefined, undefined, undefined, true);

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);
    return file;
}

/**
 * 修改工作表名称
 * @param {String} name 工作表名称
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetName (name, options = {}) {
    if (getObjType(name) != 'string') {
        return tooltip.info("The name parameter is invalid.", "");
    }

    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let oldtxt = file.name;
    file.name = name;

    $("#luckysheet-sheets-item" + file.index + " .luckysheet-sheets-item-name").text(name);

    server.saveParam("all", file.index, name, { "k": "name" });

    if (Store.clearjfundo) {
        let redo = {};
        redo.type = "sheetName";
        redo.sheetIndex = file.index;

        redo.oldtxt = oldtxt;
        redo.txt = name;

        Store.jfundo.length  = 0;
        Store.jfredo.push(redo);
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 设置工作表名称处的颜色
 * @param {String} color 工作表颜色
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetColor (color, options = {}) {
    if (getObjType(color) != 'string' || color.toString().length == 0) {
        return tooltip.info("The color parameter is invalid.", "");
    }

    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let oldcolor = file.color;
    file.color = color;

    $("#luckysheet-sheets-item" + file.index).find(".luckysheet-sheets-item-color").remove();
    $("#luckysheet-sheets-item" + file.index).append('<div class="luckysheet-sheets-item-color" style=" position: absolute; width: 100%; height: 3px; bottom: 2px; left: 0px; background-color: ' + color || 'var(--ux-primary-color, #555FFF)' + ';"></div>');

    server.saveParam("all", file.index, color, { "k": "color" });

    if (Store.clearjfundo) {
        let redo = {};
        redo.type = "sheetColor";
        redo.sheetIndex = file.index;

        redo.oldcolor = oldcolor;
        redo.color = color;

        Store.jfundo.length  = 0;
        Store.jfredo.push(redo);
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 指定工作表向左边或右边移动一个位置，或者指定索引，返回指定的工作表对象
 * @param {String | Number} type 工作表移动方向或者移动的目标索引
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetMove (type, options = {}) {
    if (type != 'left' && type != 'right' && !isRealNum(type)) {
        return tooltip.info("Type parameter not available", "");
    }

    if (isRealNum(type)) {
        type = parseInt(type);
    }

    let curOrder = getSheetIndex(Store.currentSheetIndex);
    let {
        order = curOrder,
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("ncorrect worksheet index", "");
    }

    let sheetIndex = file.index;

    if (type == 'left') {
        if (order == 0) {
            return;
        }

        let prevIndex = Store.luckysheetfile[order - 1].index;
        $("#luckysheet-sheets-item" + sheetIndex).insertBefore($("#luckysheet-sheets-item" + prevIndex));

        Store.luckysheetfile.splice(order, 1);
        Store.luckysheetfile.splice(order - 1, 0, file);
    } else if (type == 'right') {
        if (order == Store.luckysheetfile.length - 1) {
            return;
        }

        let nextIndex = Store.luckysheetfile[order + 1].index;
        $("#luckysheet-sheets-item" + sheetIndex).insertAfter($("#luckysheet-sheets-item" + nextIndex));

        Store.luckysheetfile.splice(order, 1);
        Store.luckysheetfile.splice(order + 1, 0, file);
    } else {
        if (type < 0) {
            type = 0;
        }

        if (type > Store.luckysheetfile.length - 1) {
            type = Store.luckysheetfile.length - 1;
        }

        if (type == order) {
            return;
        }

        if (type < order) {
            let prevIndex = Store.luckysheetfile[type].index;
            $("#luckysheet-sheets-item" + sheetIndex).insertBefore($("#luckysheet-sheets-item" + prevIndex));
        } else {
            let nextIndex = Store.luckysheetfile[type].index;
            $("#luckysheet-sheets-item" + sheetIndex).insertAfter($("#luckysheet-sheets-item" + nextIndex));
        }

        Store.luckysheetfile.splice(order, 1);
        Store.luckysheetfile.splice(type, 0, file);
    }

    let orders = {};

    Store.luckysheetfile.forEach((item, i, arr) => {
        arr[i].order = i;
        orders[item.index.toString()] = i;
    });

    server.saveParam("shr", null, orders);

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 重新排序所有工作表的位置，指定工作表顺序的数组。
 * @param {Array} orderList 工作表顺序，设置工作表的index和order来指定位置
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetOrder (orderList, options = {}) {
    if (orderList == null || orderList.length == 0) {
        return tooltip.info("Type orderList not available", "");
    }

    let orderListMap = {};
    orderList.forEach((item) => {
        orderListMap[item.index.toString()] = item.order;
    });

    Store.luckysheetfile.sort((x, y) => {
        let order_x = orderListMap[x.index.toString()];
        let order_y = orderListMap[y.index.toString()];

        if (order_x != null && order_y != null) {
            return order_x - order_y;
        } else if (order_x != null) {
            return -1;
        } else if (order_y != null) {
            return 1;
        } else {
            return 1;
        }
    });

    let orders = {};

    Store.luckysheetfile.forEach((item, i, arr) => {
        arr[i].order = i;
        orders[item.index.toString()] = i;

        if (i > 0) {
            let preIndex = arr[i - 1].index;
            $("#luckysheet-sheets-item" + item.index).insertAfter($("#luckysheet-sheets-item" + preIndex));
        }
    });

    server.saveParam("shr", null, orders);

    let {
        success
    } = { ...options };

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 设置工作表缩放比例
 * @param {Number} zoom 工作表缩放比例，值范围为0.1 ~ 4；
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetZoom (zoom, options = {}) {
    if (!isRealNum(zoom) || zoom < 0.1 || zoom > 4) {
        return tooltip.info("The zoom parameter is invalid.", "");
    }

    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    file.zoomRatio = zoom;

    server.saveParam("all", file.index, zoom, { "k": "zoomRatio" });

    if (file.index == Store.currentSheetIndex) {
        Store.zoomRatio = zoom;

        zoomNumberDomBind();
        zoomRefreshView();
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 显示指定下标工作表的网格线，返回操作的工作表对象
 * @param {Object} options 可选参数
 * @param {Number} options.order 需要显示网格线的工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function showGridLines (options = {}) {
    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    file.showGridLines = true;

    if (file.index == Store.currentSheetIndex) {
        Store.showGridLines = true;

        setTimeout(function () {
            luckysheetrefreshgrid();
        }, 1);
    }

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    return file;
}

/**
 * 隐藏指定下标工作表的网格线，返回操作的工作表对象
 * @param {Object} options 可选参数
 * @param {Number} options.order 需要显示网格线的工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function hideGridLines (options = {}) {
    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    file.showGridLines = false;

    if (file.index == Store.currentSheetIndex) {
        Store.showGridLines = false;

        setTimeout(function () {
            luckysheetrefreshgrid();
        }, 1);
    }

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    return file;
}

/**
 * 刷新canvas
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function refresh (options = {}) {
    // luckysheetrefreshgrid();

    let {
        success,
        isRunFormula = true
    } = { ...options };
    jfrefreshgrid(null, null, null, isRunFormula);
    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 滚动当前工作表位置
 * @param {Object} options 可选参数
 * @param {Number} options.scrollLeft 横向滚动值
 * @param {Number} options.scrollTop 纵向滚动值
 * @param {Number} options.targetRow 纵向滚动到指定的行号
 * @param {Number} options.targetColumn 横向滚动到指定的列号
 * @param {Function} options.success 操作结束的回调函数
 */
export function scroll (options = {}) {
    let {
        scrollLeft,
        scrollTop,
        targetRow,
        targetColumn,
        success
    } = { ...options };

    if (scrollLeft != null) {
        if (!isRealNum(scrollLeft)) {
            return tooltip.info("The scrollLeft parameter is invalid.", "");
        }

        $("#luckysheet-scrollbar-x").scrollLeft(scrollLeft);
    } else if (targetColumn != null) {
        if (!isRealNum(targetColumn)) {
            return tooltip.info("The targetColumn parameter is invalid.", "");
        }

        let col = Store.visibledatacolumn[targetColumn],
            col_pre = targetColumn <= 0 ? 0 : Store.visibledatacolumn[targetColumn - 1];

        $("#luckysheet-scrollbar-x").scrollLeft(col_pre);
    }

    if (scrollTop != null) {
        if (!isRealNum(scrollTop)) {
            return tooltip.info("The scrollTop parameter is invalid.", "");
        }

        $("#luckysheet-scrollbar-y").scrollTop(scrollTop);
    } else if (targetRow != null) {
        if (!isRealNum(targetRow)) {
            return tooltip.info("The targetRow parameter is invalid.", "");
        }

        let row = Store.visibledatarow[targetRow],
            row_pre = targetRow <= 0 ? 0 : Store.visibledatarow[targetRow - 1];

        $("#luckysheet-scrollbar-y").scrollTop(row_pre);
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 根据窗口大小自动resize画布
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function resize (options = {}) {
    luckysheetsizeauto();

    let {
        success
    } = { ...options };

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 返回指定选区截图后生成的base64格式的图片
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围，只能为单个选区；默认为当前选区
 */
export function getScreenshot (options = {}) {
    let {
        range = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1]
    } = { ...options };

    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        let cellrange = formula.getcellrange(range);
        range = {
            "row": cellrange.row,
            "column": cellrange.column
        };
    }

    if (getObjType(range) != 'object' || range.row == null || range.column == null) {
        return tooltip.info("The range parameter is invalid.", "");
    }

    let str = range.row[0],
        edr = range.row[1],
        stc = range.column[0],
        edc = range.column[1];

    let has_PartMC = hasPartMC(Store.config, str, edr, stc, edc);

    if (has_PartMC) {
        return tooltip.info('Cannot perform this operation on partially merged cells', '');
    }

    let visibledatarow = Store.visibledatarow;
    let visibledatacolumn = Store.visibledatacolumn;

    let scrollHeight, rh_height;
    if (str - 1 < 0) {
        scrollHeight = 0;
        rh_height = visibledatarow[edr];
    } else {
        scrollHeight = visibledatarow[str - 1];
        rh_height = visibledatarow[edr] - visibledatarow[str - 1];
    }

    let scrollWidth, ch_width;
    if (stc - 1 < 0) {
        scrollWidth = 0;
        ch_width = visibledatacolumn[edc];
    } else {
        scrollWidth = visibledatacolumn[stc - 1];
        ch_width = visibledatacolumn[edc] - visibledatacolumn[stc - 1];
    }

    let newCanvas = $("<canvas>").attr({
        width: Math.ceil(ch_width * Store.devicePixelRatio),
        height: Math.ceil(rh_height * Store.devicePixelRatio)
    }).css({ width: ch_width, height: rh_height });

    luckysheetDrawMain(scrollWidth, scrollHeight, ch_width, rh_height, 1, 1, null, null, newCanvas);
    let ctx_newCanvas = newCanvas.get(0).getContext("2d");

    // 补上 左边框和上边框
    ctx_newCanvas.beginPath();
    ctx_newCanvas.moveTo(
        0,
        0
    );
    ctx_newCanvas.lineTo(
        0,
        Store.devicePixelRatio * rh_height
    );
    ctx_newCanvas.lineWidth = Store.devicePixelRatio * 2;
    ctx_newCanvas.strokeStyle = luckysheetdefaultstyle.strokeStyle;
    ctx_newCanvas.stroke();
    ctx_newCanvas.closePath();

    ctx_newCanvas.beginPath();
    ctx_newCanvas.moveTo(
        0,
        0
    );
    ctx_newCanvas.lineTo(
        Store.devicePixelRatio * ch_width,
        0
    );
    ctx_newCanvas.lineWidth = Store.devicePixelRatio * 2;
    ctx_newCanvas.strokeStyle = luckysheetdefaultstyle.strokeStyle;
    ctx_newCanvas.stroke();
    ctx_newCanvas.closePath();

    let url = newCanvas.get(0).toDataURL("image/png");

    return url;
}

/**
 * 设置工作簿名称
 * @param {String} name 工作簿名称
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function setWorkbookName (name, options = {}) {
    if (name == null || name.toString().length == 0) {
        return tooltip.info("The name parameter is invalid.", "");
    }

    $("#luckysheet_info_detail_input").val(name);

    let {
        success
    } = { ...options };

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 获取工作簿名称
 * @param   {Object}    options         可选参数
 * @param   {Function}  options.success 操作结束的回调函数
 * @returns {String}    返回工作簿名称，如果读取失败则返回空字符串并弹窗提示
 */
export function getWorkbookName (options = {}) {
    let name = "";
    let element = $("#luckysheet_info_detail_input");

    if (element.length == 0) {
        tooltip.info('Failed to get workbook name, label loading failed!');
        return name;
    }

    name = $.trim(element.val());

    let {
        success
    } = { ...options };

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    return name;
}

/**
 * 撤销当前操作，返回刚刚撤销的操作对象
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function undo (options = {}) {
    let ctr = $.extend(true, {}, Store.jfredo[Store.jfredo.length - 1]);

    controlHistory.redo(new Event('custom'));
    luckysheetactiveCell();

    let {
        success
    } = { ...options };

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    return ctr;
}

/**
 * 重做当前操作，返回刚刚重做的操作对象
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function redo (options = {}) {
    let ctr = $.extend(true, {}, Store.jfundo[Store.jfundo.length - 1]);

    controlHistory.undo(new Event('custom'));
    luckysheetactiveCell();

    let {
        success
    } = { ...options };

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    return ctr;
}

/**
 * 返回所有工作表配置
 */
export function getAllSheets () {
    let data = Store.luckysheetfile || [];
    let oldIndex = Store.currentSheetIndex;
    data.forEach((item, index, arr) => {
        if (!item.data.length) {
            item.data = buildGridData(item);
        }
        let remainData = [];
        item.data = [...item.data, ...remainData];
        if (item.data != null && item.data.length > 0) {
            item.celldata = sheetmanage.getGridData(item.data);
        }
        delete item.load;
    });
    sheetmanage.changeSheet(oldIndex);
    return data;
}

/**
 * 根据index获取sheet页配置
 *
 * @param {Object} options 可选参数
 * @param {String} options.index 工作表index
 * @param {Number} options.order 工作表order
 * @param {String} options.name 工作表name
 */
export function getSheet (options = {}) {
    let {
        index,
        order,
        name
    } = { ...options };
    let file;
    if (index != null) {
        file = sheetmanage.getSheetByIndex(index);
    } else if (order != null) {
        file = Store.luckysheetfile[order];
    } else if (name != null) {
        file = sheetmanage.getSheetByName(name);
    }
    if (file && index != null) {
        file.data = [...(file.data || [])];
        return file;
    }
    return sheetmanage.getSheetByIndex();
}

/**
 * 快捷返回指定工作表的数据
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 */
export function getSheetData (options = {}) {
    let {
        order = getSheetIndex(Store.currentSheetIndex)
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let data = $.extend(true, [], file.data);

    if (data == null || data.length == 0) {
        data = $.extend(true, [], sheetmanage.buildGridData(file));
    }

    return data;
}

/**
 * 快捷返回指定工作表的config配置
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 */
export function getConfig (options = {}) {
    let {
        order = getSheetIndex(Store.currentSheetIndex)
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let config = $.extend(true, {}, file.config);

    return config;
}

/**
 * 快捷设置指定工作表config配置
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setConfig (cfg, options = {}) {
    if (getObjType(cfg) != 'object') {
        return tooltip.info("The cfg parameter is invalid.", "");
    }

    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    file.config = cfg;

    if (file.index == Store.currentSheetIndex) {
        Store.config = cfg;

        if ("rowhidden" in cfg || "colhidden" in cfg || "rowlen" in cfg || "columnlen" in cfg) {
            jfrefreshgrid_rhcw(Store.flowdata.length, Store.flowdata[0].length);
        }

        setTimeout(function () {
            luckysheetrefreshgrid();
        }, 1);
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 返回所有表格数据结构的一维数组luckysheetfile
 */
export function getLuckysheetfile () {
    return getluckysheetfile();
}

/**
 * 指定工作表范围设置数据验证功能，并设置参数
 * @param {Object} optionItem 数据验证的配置信息
 * @param {String} optionItem.type 类型
 * @param {String | Null} optionItem.type2 条件类型
 * @param {String | Number} optionItem.value1 条件值1
 * @param {String | Number} optionItem.value2 条件值2
 * @param {Boolean} optionItem.checked 选中状态
 * @param {Boolean} optionItem.remote 自动远程获取选项
 * @param {Boolean} optionItem.prohibitInput 输入数据无效时禁止输入
 * @param {Boolean} optionItem.hintShow 选中单元格时显示提示语
 * @param {String} optionItem.hintText 提示语文本
 * @param {Object} options 可选参数
 * @param {Array | Object | String} options.range 选区范围；默认为当前选区
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setDataVerification (optionItem, options = {}) {
    if (getObjType(optionItem) != 'object') {
        return tooltip.info("The optionItem parameter is invalid.", "");
    }

    let {
        type,
        type2 = null,
        value1 = '',
        value2 = '',
        remote = false,
        prohibitInput = false,
        hintShow = false,
        hintText = '',
        checked = false
    } = { ...optionItem };

    let typeValues = ["dropdown", "checkbox", "number", "number_integer", "number_decimal", "text_content", "text_length", "date", "validity"];
    let type2Values_1 = ["bw", "nb", "eq", "ne", "gt", "lt", "gte", "lte"];
    let type2Values_2 = ["include", "exclude", "equal"];
    let type2Values_3 = ["bw", "nb", "eq", "ne", "bf", "nbf", "af", "naf"];
    let type2Values_4 = ["card", "phone"];

    if (!typeValues.includes(type)) {
        return tooltip.info("The optionItem.type parameter is invalid.", "");
    }

    let dvText = locale().dataVerification;

    if (type == 'dropdown') {
        if (value1.length == 0) {
            tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo1);
            return;
        }
    } else if (type == 'checkbox') {
        if (value1.length == 0 || value2.length == 0) {
            tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo2);
            return;
        }
    } else if (type == 'number' || type == 'number_integer' || type == 'number_decimal') {
        if (!type2Values_1.includes(type2)) {
            return tooltip.info("The optionItem.type2 parameter is invalid.", "");
        }

        if (!isRealNum(value1)) {
            tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo3);
            return;
        }

        if (type2 == 'bw' || type2 == 'nb') {
            if (!isRealNum(value2)) {
                tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo3);
                return;
            }

            if (Number(value2) < Number(value1)) {
                tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo4);
                return;
            }
        }
    } else if (type == 'text_content') {
        if (!type2Values_2.includes(type2)) {
            return tooltip.info("The optionItem.type2 parameter is invalid.", "");
        }

        if (value1.length == 0) {
            tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo5);
            return;
        }
    } else if (type == 'text_length') {
        if (!type2Values_1.includes(type2)) {
            return tooltip.info("The optionItem.type2 parameter is invalid.", "");
        }

        if (!isRealNum(value1)) {
            tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo3);
            return;
        }

        if (type2 == 'bw' || type2 == 'nb') {
            if (!isRealNum(value2)) {
                tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo3);
                return;
            }

            if (Number(value2) < Number(value1)) {
                tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo4);
                return;
            }
        }
    } else if (type == 'date') {
        if (!type2Values_3.includes(type2)) {
            return tooltip.info("The optionItem.type2 parameter is invalid.", "");
        }

        if (!isdatetime(value1)) {
            tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo6);
            return;
        }

        if (type2 == 'bw' || type2 == 'nb') {
            if (!isdatetime(value2)) {
                tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo6);
                return;
            }

            if (diff(value1, value2) > 0) {
                tooltip.info('<i class="fa fa-exclamation-triangle"></i>', dvText.tooltipInfo7);
                return;
            }
        }
    } else if (type == 'validity') {
        if (!type2Values_4.includes(type2)) {
            return tooltip.info("The optionItem.type2 parameter is invalid.", "");
        }
    }

    if (getObjType(remote) != 'boolean') {
        return tooltip.info("The optionItem.remote parameter is invalid.", "");
    }

    if (getObjType(prohibitInput) != 'boolean') {
        return tooltip.info("The optionItem.prohibitInput parameter is invalid.", "");
    }

    if (getObjType(hintShow) != 'boolean') {
        return tooltip.info("The optionItem.hintShow parameter is invalid.", "");
    }

    let {
        range = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1],
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        let cellrange = formula.getcellrange(range);
        range = {
            "row": cellrange.row,
            "column": cellrange.column
        };
    }

    if (getObjType(range) != 'object' || range.row == null || range.column == null) {
        return tooltip.info("The range parameter is invalid.", "");
    }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let item = {
        type: type,
        type2: type2,
        value1: value1,
        value2: value2,
        checked: checked,
        remote: remote,
        prohibitInput: prohibitInput,
        hintShow: hintShow,
        hintText: hintText
    };

    let currentDataVerification = $.extend(true, {}, file.dataVerification);

    let data = $.extend(true, [], file.data);
    if (data.length == 0) {
        data = sheetmanage.buildGridData(file);
    }

    let str = range.row[0],
        edr = range.row[1],
        stc = range.column[0],
        edc = range.column[1];

    for (let r = str; r <= edr; r++) {
        for (let c = stc; c <= edc; c++) {
            currentDataVerification[r + '_' + c] = item;

            if (type == 'checkbox') {
                item.checked ? setcellvalue(r, c, data, item.value1) : setcellvalue(r, c, data, item.value2);
            }
        }
    }

    if (file.index == Store.currentSheetIndex) {
        let historyDataVerification = $.extend(true, {}, file.dataVerification);

        if (type == 'checkbox') {
            dataVerificationCtrl.refOfCheckbox(historyDataVerification, currentDataVerification, Store.currentSheetIndex, data, range);
        } else {
            dataVerificationCtrl.ref(historyDataVerification, currentDataVerification, Store.currentSheetIndex);
        }
    } else {
        file.dataVerification = currentDataVerification;
        file.data = data;
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 指定工作表范围删除数据验证功能
 * @param {Object} options 可选参数
 * @param {Array | Object | String} options.range 选区范围；默认为当前选区
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteDataVerification (options = {}) {
    let {
        range = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1],
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        let cellrange = formula.getcellrange(range);
        range = {
            "row": cellrange.row,
            "column": cellrange.column
        };
    }

    if (getObjType(range) != 'object' || range.row == null || range.column == null) {
        return tooltip.info("The range parameter is invalid.", "");
    }

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let currentDataVerification = $.extend(true, {}, file.dataVerification);

    let str = range.row[0],
        edr = range.row[1],
        stc = range.column[0],
        edc = range.column[1];

    for (let r = str; r <= edr; r++) {
        for (let c = stc; c <= edc; c++) {
            delete currentDataVerification[r + '_' + c];
        }
    }

    if (file.index == Store.currentSheetIndex) {
        let historyDataVerification = $.extend(true, {}, file.dataVerification);
        dataVerificationCtrl.ref(historyDataVerification, currentDataVerification, Store.currentSheetIndex);
    } else {
        file.dataVerification = currentDataVerification;
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 在指定的工作表中指定单元格位置插入图片
 * @param {String} src 图片src
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Number} options.rowIndex 要插入图片的单元格行下标；默认为0
 * @param {Number} options.colIndex 要插入图片的单元格列下标；默认为0
 * @param {Function} options.success 操作结束的回调函数
 */
export function insertImage (src, options = {}) {
    let {
        order = getSheetIndex(Store.currentSheetIndex),
        rowIndex,
        colIndex,
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    if (file.index == Store.currentSheetIndex) {
        let last = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1];

        if (rowIndex == null) {
            rowIndex = last.row_focus || 0;
        }

        if (rowIndex < 0) {
            rowIndex = 0;
        }

        if (rowIndex > Store.visibledatarow.length) {
            rowIndex = Store.visibledatarow.length;
        }

        if (colIndex == null) {
            colIndex = last.column_focus || 0;
        }

        if (colIndex < 0) {
            colIndex = 0;
        }

        if (colIndex > Store.visibledatacolumn.length) {
            colIndex = Store.visibledatacolumn.length;
        }

        let left = colIndex == 0 ? 0 : Store.visibledatacolumn[colIndex - 1];
        let top = rowIndex == 0 ? 0 : Store.visibledatarow[rowIndex - 1];

        let image = new Image();
        image.onload = function () {
            let width = image.width,
                height = image.height;

            let img = {
                src: src,
                left: left,
                top: top,
                originWidth: width,
                originHeight: height
            };

            imageCtrl.addImgItem(img);

            if (success && typeof success === 'function') {
                success();
            }
        };
        image.src = src;
    } else {
        let images = file.images || {};
        let config = file.config;
        let zoomRatio = file.zoomRatio || 1;

        let rowheight = file.row;
        let visibledatarow = file.visibledatarow || [];
        if (visibledatarow.length === 0) {
            let rh_height = 0;

            for (let r = 0; r < rowheight; r++) {
                let rowlen = Store.defaultrowlen;

                if (config.rowlen != null && config.rowlen[r] != null) {
                    rowlen = config.rowlen[r];
                }

                if (config.rowhidden != null && config.rowhidden[r] != null) {
                    visibledatarow.push(rh_height);
                    continue;
                }

                rh_height += Math.round((rowlen + 1) * zoomRatio);

                visibledatarow.push(rh_height); // 行的临时长度分布
            }
        }

        let colwidth = file.column;
        let visibledatacolumn = file.visibledatacolumn || [];
        if (visibledatacolumn.length === 0) {
            let ch_width = 0;

            for (let c = 0; c < colwidth; c++) {
                let firstcolumnlen = Store.defaultcollen;

                if (config.columnlen != null && config.columnlen[c] != null) {
                    firstcolumnlen = config.columnlen[c];
                }

                if (config.colhidden != null && config.colhidden[c] != null) {
                    visibledatacolumn.push(ch_width);
                    continue;
                }

                ch_width += Math.round((firstcolumnlen + 1) * zoomRatio);

                visibledatacolumn.push(ch_width);// 列的临时长度分布
            }
        }

        if (rowIndex == null) {
            rowIndex = 0;
        }

        if (rowIndex < 0) {
            rowIndex = 0;
        }

        if (rowIndex > visibledatarow.length) {
            rowIndex = visibledatarow.length;
        }

        if (colIndex == null) {
            colIndex = 0;
        }

        if (colIndex < 0) {
            colIndex = 0;
        }

        if (colIndex > visibledatacolumn.length) {
            colIndex = visibledatacolumn.length;
        }

        let left = colIndex == 0 ? 0 : visibledatacolumn[colIndex - 1];
        let top = rowIndex == 0 ? 0 : visibledatarow[rowIndex - 1];

        let image = new Image();
        image.onload = function () {
            let img = {
                src: src,
                left: left,
                top: top,
                originWidth: image.width,
                originHeight: image.height
            };

            let width, height;
            let max = 400;

            if (img.originHeight < img.originWidth) {
                height = Math.round(img.originHeight * (max / img.originWidth));
                width = max;
            } else {
                width = Math.round(img.originWidth * (max / img.originHeight));
                height = max;
            }

            let imgItem = $.extend(true, {}, imageCtrl.imgItem);
            imgItem.src = img.src;
            imgItem.originWidth = img.originWidth;
            imgItem.originHeight = img.originHeight;
            imgItem.default.width = width;
            imgItem.default.height = height;
            imgItem.default.left = img.left;
            imgItem.default.top = img.top;
            imgItem.crop.width = width;
            imgItem.crop.height = height;

            let id = imageCtrl.generateRandomId();
            images[id] = imgItem;

            file.images = images;

            if (success && typeof success === 'function') {
                success();
            }
        };
        image.src = src;
    }
}

/**
 * 删除指定工作表中的图片
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {String | Array} options.idList 要删除图片的id集合，也可为字符串`"all"`，all为所有的字符串；默认为`"all"`
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteImage (options = {}) {
    let {
        order = getSheetIndex(Store.currentSheetIndex),
        idList = 'all',
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    let images = file.images;

    if (images == null) {
        return tooltip.info("The worksheet has no pictures to delete.", "");
    }

    if (idList != 'all' && getObjType(idList) != 'array') {
        return tooltip.info("The idList parameter is invalid.", "");
    }

    if (getObjType(idList) == 'array') {
        idList.forEach(item => {
            delete images[item];
        });
    } else {
        images = null;
    }

    file.images = images;

    if (file.index == Store.currentSheetIndex) {
        if (imageCtrl.currentImgId != null && (idList == 'all' || idList.includes(imageCtrl.currentImgId))) {
            $("#luckysheet-modal-dialog-activeImage").hide();
            $("#luckysheet-modal-dialog-cropping").hide();
            $("#luckysheet-modal-dialog-slider-imageCtrl").hide();
        }

        imageCtrl.images = images;
        imageCtrl.allImagesShow();
        imageCtrl.init();
    }

    if (success && typeof success === 'function') {
        success();
    }
}

/**
 * 获取指定工作表的图片配置
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function getImageOption (options = {}) {
    let {
        order = getSheetIndex(Store.currentSheetIndex),
        success
    } = { ...options };

    let file = Store.luckysheetfile[order];

    if (file == null) {
        return tooltip.info("The order parameter is invalid.", "");
    }

    setTimeout(function () {
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);

    return file.images;
}

/**
 * data => celldata ，data二维数组数据转化成 {r, c, v}格式一维数组
 *
 * @param {Array} data 二维数组数据
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function transToCellData (data, options = {}) {
    let {
        success
    } = { ...options };

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 0);

    return sheetmanage.getGridData(data);
}

/**
 * celldata => data ，celldata一维数组数据转化成表格所需二维数组
 *
 * @param {Array} celldata 二维数组数据
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function transToData (celldata, options = {}) {
    let {
        success
    } = { ...options };

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    }, 0);

    return sheetmanage.buildGridData({
        celldata: celldata
    });
}

/**
 * 导出的json字符串可以直接当作`luckysheet.create(options)`初始化工作簿时的参数`options`使用
 *
 */
export function toJson () {
    const toJsonOptions = Store.toJsonOptions;

    // Workbook name
    toJsonOptions.title = $("#luckysheet_info_detail_input").val();

    toJsonOptions.data = getAllSheets();

    // row and column
    getluckysheetfile().forEach((file, index) => {
        if (file.data == undefined) {
            return;
        }
        toJsonOptions.data[index].row = getObjType(file.data) === 'array' ? file.data.length : 0;
        toJsonOptions.data[index].column = getObjType(file.data[0]) === 'array' ? file.data[0].length : 0;
    });

    return toJsonOptions;
}

/**
 * 传入目标语言，切换到对应的语言界面
 * @param {String} lang 可选参数；暂支持`"zh"`、`"en"`、`"es"`；默认为`"zh"`；
 */
export function changLang (lang = 'zh') {
    if (!['zh', 'en', 'es'].includes(lang)) {
        return tooltip.info("The lang parameter is invalid.", "");
    }

    let options = toJson();
    options.lang = lang;
    luckysheet.create(options);
}

/**
 * 关闭websocket连接
 */
export function closeWebsocket () {
    if (server.websocket == null) {
        return;
    }
    server.websocket.close(1000);
}

/**
 * 根据范围字符串转换为range数组
 * @param {String} txt 范围字符串
 */
export function getRangeByTxt (txt) {
    // 默认取当前第一个范围
    if (txt == null) {
        return {
            column: Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1].column,
            row: Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1].row
        };
    }

    const range = conditionformat.getRangeByTxt(txt);

    return {
        column: range[0].column,
        row: range[0].row
    };
}

/**
 * 根据范围数组转换为范围字符串
 * @param {Object | Array} range 范围数组
 */
export function getTxtByRange (range = Store.luckysheet_select_save) {
    // 单个范围
    if (getObjType(range) === 'object') {
        range = [range];
    }
    return conditionformat.getTxtByRange(range);
}

/**
 * 初始化分页器
 * @param {Object} config 分页器配置
 * @param {Number} config.pageIndex 当前的页码
 * @param {Number} config.pageSize 每页显示多少条数据
 * @param {Array} config.selectOption 选择每页的条数
 * @param {Number} config.total 总条数
 */
export function pagerInit (config) {
    const { prevPage, nextPage, total } = locale().button;
    $('#luckysheet-bottom-pager').remove();
    $('#luckysheet-sheet-content').after('<div id="luckysheet-bottom-pager" style="font-size: 14px; margin-left: 10px; display: inline-block;"></div>');
    $("#luckysheet-bottom-pager").sPage({
        page: config.pageIndex, // 当前页码，必填
        total: config.total, // 数据总条数，必填
        selectOption: config.selectOption, // 选择每页的行数，
        pageSize: config.pageSize, // 每页显示多少条数据，默认10条
        showTotal: config.showTotal, // 是否显示总数，默认关闭：false
        showSkip: config.showSkip, // 是否显示跳页，默认关闭：false
        showPN: config.showPN, // 是否显示上下翻页，默认开启：true
        prevPage: config.prevPage || prevPage, // 上翻页文字描述，默认"上一页"
        nextPage: config.nextPage || nextPage, // 下翻页文字描述，默认"下一页"
        totalTxt: config.totalTxt || total + config.total, // 数据总条数文字描述，{total}为占位符，默认"总共：{total}"
        backFun: function (page) {
            page.pageIndex = page.page;
            if (!method.createHookFunction("onTogglePager", page)) {  }
        }
    });
}

/**
 * 刷新公式
 * @param {Function} success 回调函数
 */
export function refreshFormula (success) {
    formula.execFunctionGroupForce(true);
    luckysheetrefreshgrid();
    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    });
}

/**
 * 更新sheet数据
 * @param {Array} data 工作簿配置，可以包含多个表
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 *
 */
export function updataSheet (options = {}) {
    let { data, success } = options;
    let files = Store.luckysheetfile;
    for (let i = 0; i < data.length; i++) {
        for (let j = 0; j < files.length; j++) {
            if (files[j].index === data[i].index) {
                files[j] = data[i];
            }
        }
    }
    let file = files[sheetmanage.getSheetIndex(Store.currentSheetIndex)],
        sheetData = sheetmanage.buildGridData(file);
    file.data = sheetData;

    if (!!file.isPivotTable) {
        Store.luckysheetcurrentisPivotTable = true;
        if (!isPivotInitial) {
            pivotTable.changePivotTable(index);
        }
    } else {
        Store.luckysheetcurrentisPivotTable = false;
        $("#luckysheet-modal-dialog-slider-pivot").hide();
        luckysheetsizeauto(false);
    }
    sheetmanage.mergeCalculation(file.index);
    sheetmanage.setSheetParam();
    setTimeout(function () {
        sheetmanage.showSheet();
        sheetmanage.restoreCache();
        formula.execFunctionGroupForce(luckysheetConfigsetting.forceCalculation);
        sheetmanage.restoreSheetAll(Store.currentSheetIndex);
        luckysheetrefreshgrid();
        if (success && typeof success === 'function') {
            success();
        }
    }, 1);
    server.saveParam("shs", null, Store.currentSheetIndex);
}

/**
 * 刷新状态栏的状态
 * @param {Array}  data             操作数据
 * @param {Number} r                指定的行
 * @param {Number} c                指定的列
 * @param {Function} success        回调函数
 */
export function refreshMenuButtonFocus (data, r, c, success) {
    data = data || Store.flowdata;
    if (r == null && c == null) {
        /* 获取选取范围 */
        let last = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1];

        r = last.row_focus || last.row[0];
        c = last.column_focus || last.column[0];
    }

    menuButton.menuButtonFocus(data, r, c);

    setTimeout(() => {
        if (success && typeof success === 'function') {
            success();
        }
    });
}

/**
 * 检查选区内所有cell指定类型的状态是否满足条件（主要是粗体、斜体、删除线和下划线等等）
 * @param {String}  type            类型
 * @param {String}  status          目标状态值
 */
export function checkTheStatusOfTheSelectedCells (type, status) {
    /* 获取选区内所有的单元格-扁平后的处理 */
    let cells = getRangeWithFlatten();

    let flag = cells.every(({ r, c }) => {
        let cell = Store.flowdata[r][c];
        if (cell == null) {
            return false;
        }
        return cell[type] == status;
    });

    return flag;
}

function modifieDataDiff (file) {
    let flowData  = file.data;
    let updateCells = [];
    if (flowData.length <= 0) {
        return {
            updateCells
        };
    };
    for (let i = 0; i < flowData.length; i++) {
        for (let j = 0; j < flowData[0].length; j++) {
            let emptyCell = {
                r: i,
                c: j
            };
            // 原始单元格没数据 但当前单元格有数据
            if (isNil(flowData[i][j])) continue;
            if (flowData[i][j].ct && flowData[i][j].ct.s && flowData[i][j].ct.s.length) {
                const concatV = flowData[i][j].ct.s.map((ss) => ss.v).join('');
                if (concatV !== flowData[i][j].ov) {
                    updateCells.push({
                        r: i,
                        c: j,
                        ...flowData[i][j],
                        v: concatV
                    });
                }
                continue;
            }
            if (isNil(flowData[i][j].ov) && isNil(flowData[i][j].of) && (!isNil(flowData[i][j].v) || !isNil(flowData[i][j].f))) {
                updateCells.push({
                    r: i,
                    c: j,
                    ...flowData[i][j]
                });
                continue;
            }
            // 当前单元格没数据 但对应原始数据单元格有数据
            if (isNil(flowData[i][j].f) && isNil(flowData[i][j].v) && (!isNil(flowData[i][j].ov) || !isNil(flowData[i][j].of))) {
                updateCells.push({
                    r: i,
                    c: j,
                    ...flowData[i][j]
                });
                continue;
            }
            // 原始单元格 当前单元格都有值 且不相等

            // 有公式的公式优先
            if (flowData[i][j].f || flowData[i][j].of) {
                // 公式不相等 或者在原始值有值的情况下  v 不相等
                if (
                    flowData[i][j].f != flowData[i][j].of || (!isNil(flowData[i][j].ov) && flowData[i][j].v != flowData[i][j].ov)
                ) {
                    updateCells.push({
                        r: i,
                        c: j,
                        ...flowData[i][j]
                    });
                }
            } else {
                // 只对值进行比较   当两者不全为空时 且 两者不相等
                if ((!isNil(flowData[i][j].v) || !isNil(flowData[i][j].ov)) && flowData[i][j].v != flowData[i][j].ov) {
                    updateCells.push({
                        r: i,
                        c: j,
                        ...flowData[i][j]
                    });
                }
            }
        }
    }
    return {
        updateCells: updateCells
    };
}

export async function resetOriginData (resetData) {
    if (resetData) {
        for (let i = 0; i < resetData.length; i++) {
            const { sheetIndex, data } = resetData[i];
            const file = Store.luckysheetfile[sheetIndex];
            for (let j = 0; j < data.length; j++) {
                const { r, c, v, ct } = data[j];
                file.data[r][c].ov = v;
                if (ct && ct.s && ct.s.length) {
                    let concatV = ct.s.map((s) => s.v).join('');
                    file.data[r][c].ov = concatV;
                }
            }
            for (let rowData of file.data) {
                for (let columnData of rowData) {
                    if (columnData) delete columnData.isInsert;
                }
            }
        }
    } else {
        for (let i = 0; i < Store.luckysheetfile.length; i++) {
            const file = Store.luckysheetfile[i];
            let data = file.data;
            if (file.data.length < 1) {
                file.data = sheetmanage.buildGridData(file, false, false);
            }
            for (let rowData of file.data) {
                for (let columnData of rowData) {
                    if (columnData) delete columnData.isInsert;
                }
            }
        }
    }
}

export function refreshOriginData (refreshData = []) {
    refreshData.forEach((data) => {
        const { sheetIndex, whiteList = {}, blackList = {} } = data;
        const file = Store.luckysheetfile[sheetIndex];
        const whiteKeys = Object.keys(whiteList);
        if (whiteKeys.length) {
            // 白名单优先
            for (let j = 0; j < whiteKeys.length; j++) {
                const rc = whiteKeys[j].split('_').map(v => Number(v));
                if (file.data[rc[0]][rc[1]]) file.data[rc[0]][rc[1]].ov = file.data[rc[0]][rc[1]].v;
                if (file.data[rc[0]][rc[1]] && file.data[rc[0]][rc[1]].ct && file.data[rc[0]][rc[1]].ct.s && file.data[rc[0]][rc[1]].ct.s.length) {
                    let concatV = file.data[rc[0]][rc[1]].ct.s.map((s) => s.v).join('');
                    file.data[rc[0]][rc[1]].ov = concatV;
                }
                if (file.data[rc[0]][rc[1]]) file.data[rc[0]][rc[1]].of = file.data[rc[0]][rc[1]].f;
            }
        } else if (file.data && file.data.length) {
            // 黑白名单都为空 或者白名单为空
            for (let i = 0; i < file.data.length; i++) {
                for (let j = 0; j < file.data[0].length; j++) {
                    if (!blackList[`${i}_${j}`] && file.data[i][j]) {
                        file.data[i][j].ov = file.data[i][j].v;
                        if (file.data[i][j].ct &&  file.data[i][j].ct.s &&  file.data[i][j].ct.s.length) {
                            let concatV = file.data[i][j].ct.s.map((s) => s.v).join('');
                            file.data[i][j].ov = concatV;
                        }
                        file.data[i][j].of = file.data[i][j].f;
                    }
                }
            }
        }
    });
}

export async function getUpdateCells (sheetIndexList) {
    let allUpdateCells = {};
    const indexList = sheetIndexList || Store.luckysheetfile.map((k, i) => i);
    for (let i = 0; i < indexList.length; i++) {
        const sheet = Store.luckysheetfile[indexList[i]];
        const updateCells = modifieDataDiff(sheet);
        allUpdateCells[indexList[i]] = updateCells;
    }
    return allUpdateCells;
}

// 获取表格内容矩形区域边界坐标
export function getContentCoordinate (order) {
    let sheetOrder = method.getInputSheetIndex(order);
    let index = method.getIndexByOrder(sheetOrder);
    return Store.conentCoordinate[index];
}

export function refreshCurrentSheet () {
    sheetmanage.storeSheetParam();
    luckysheetrefreshgrid();
}

export function getRowCount (sheetIndex) {
    let index =  method.getInputSheetIndex(sheetIndex);
    let file = Store.luckysheetfile[index];
    let rowLen = file.row;
    const sheet = sheetmanage.getSheetByIndex(file.index);
    if (!rowLen) rowLen = getMaxRowLen(sheet.celldata);
    return rowLen;
}

export function getColumnCount (sheetIndex) {
    let index =  method.getInputSheetIndex(sheetIndex);
    let file = Store.luckysheetfile[index];
    let columnLen = file.column;
    const sheet = sheetmanage.getSheetByIndex(file.index);
    if (!columnLen) columnLen = getMaxColumnLen(sheet.celldata);
    return columnLen;
}

export function getCurrentSheetIndex () {
    return getSheetIndex(Store.currentSheetIndex) || 0;
}

export function setHighLightArea (range, option = {}) {
    const {
        color = '#B6D7A8',
        type = 'border',
        index = getSheetIndex(Store.currentSheetIndex),
        use = ''
    } = option;
    let file = Store.luckysheetfile[index];
    file.config = jsondiffpatch.clone(file.config);
    let isArray;
    isArray = $.isArray(file.config.maskInfo);
    if (!isArray) file.config.maskInfo = [];
    if (!file.config.maskInfo) file.config.maskInfo = [];
    file.config.maskInfo.push({
        type,
        color,
        range,
        use
    });
    if (index === getSheetIndex(Store.currentSheetIndex)) {
        Store.config = file.config;
        refreshCurrentSheet();
    }
}
export function clearHighLightArea (use = '', sheetIndex = getSheetIndex(Store.currentSheetIndex)) {
    if (!(Store.flowdata && Store.flowdata[0])) return;
    let file = Store.luckysheetfile[sheetIndex];
    file.config.maskInfo = (file.config.maskInfo || []).filter((m) => m.use !== use);
    refreshCurrentSheet();
}
function luckysheetsheetnameeditor ($t) {
    if (Store.allowEdit === false) {
        return;
    }
    $t.attr("contenteditable", "true").addClass("luckysheet-mousedown-cancel").data("oldtxt", $t.text());
    setTimeout(function () {
        selectTextDom($t.get(0));
    }, 1);
};

export function renameSheet (sheetOrder) {
    let sheetIndex = Store.luckysheetfile[sheetOrder].index;
    let luckysheetcurrentSheetitem = $(`#luckysheet-sheets-item${sheetIndex}`);
    luckysheetsheetnameeditor(luckysheetcurrentSheetitem.find("span.luckysheet-sheets-item-name"));
}

export function deleteSheet (sheetOrder) {
    let sheetIndex = Store.luckysheetfile[sheetOrder].index;
    return sheetmanage.deleteSheet(sheetIndex);
}

export function activeArea (sr, sc, w, h) {
    setRangeShow([{
        row: [+sr, +sr + h - 1],
        column: [+sc, +sc + w - 1]
    }], { noMerge: true });
    $('#luckysheet-cell-selected-focus').hide();
}

export function clearActiveArea (refresh = true) {
    Store.luckysheet_select_save = [];
    $("#luckysheet-cols-h-selected, #luckysheet-rows-h-selected").hide();
    selectHightlightShow();
    if (refresh) {
        refreshCurrentSheet();
    }
}

/**
 * 判断指定单元格是否在可视区域
 * @param {*} param0
 */
export function isCellVisible ({
    r, c, positionText, fullCell
}) {
    let $t = $("#luckysheet-cell-main");
    let scrollLeft = $("#luckysheet-scrollbar-x").scrollLeft(),
        scrollTop = $("#luckysheet-scrollbar-y").scrollTop();
    let row = r;
    let col = c;
    // C11 转为  2, 10
    if (positionText) {
        row = parseInt(positionText.replace(/[^0-9]/g, "")) - 1;
        col = ABCatNum(positionText.replace(/[^A-Za-z]/g, ""));
    }
    // 冻结列的宽度
    let freezeLeft = luckysheetFreezen.freezenverticaldata ? luckysheetFreezen.freezenverticaldata[0] : 0;
    // 冻结行的宽度
    let freezeTop = luckysheetFreezen.freezenhorizontaldata ? luckysheetFreezen.freezenhorizontaldata[0] : 0;
    let left = freezeLeft + scrollLeft;
    let top = freezeTop + scrollTop;
    let rowVisible = false;
    let colVisible = false;
    // 冻结行 rowVisible 设置为true
    if (luckysheetFreezen.freezenhorizontaldata && row < luckysheetFreezen.freezenhorizontaldata[1]) {
        rowVisible = true;
    } else {
        let rowPosition = Store.visibledatarow[row];
        let preRowPosition = Store.visibledatarow[row - 1];
        if (rowPosition >= top && preRowPosition - scrollTop + freezeTop < $t.height()) {
            rowVisible = true;
        }
    }
    // 冻结列 colVisible 设置为true
    if (luckysheetFreezen.freezenverticaldata && col < luckysheetFreezen.freezenverticaldata[1]) {
        colVisible = true;
    } else {
        let colPosition = Store.visibledatacolumn[col];
        let preColPosition = Store.visibledatacolumn[col - 1];
        if (preColPosition >= left && colPosition - scrollLeft < $t.width()) {
            colVisible = true;
        }
    }
    return rowVisible && colVisible;
}

/**
 *  获取指定单元格在 canvas 中的位置
 * @param {*} param0
 * @returns  { x, y }
 */
export function getCellPostion ({
    r, c, positionText
}) {
    let $t = $("#luckysheet-cell-main");
    let row = r;
    let col = c;
    // C11 转为  2, 10
    if (positionText) {
        row = parseInt(positionText.replace(/[^0-9]/g, "")) - 1;
        col = ABCatNum(positionText.replace(/[^A-Za-z]/g, ""));
    }
    let scrollWidth = $t.scrollLeft();
    let scrollHeight = $t.scrollTop();
    let start_r = Store.visibledatarow[row - 1] - scrollHeight - 1;
    let start_c = Store.visibledatacolumn[col - 1] - scrollWidth;
    return {
        start_r,
        start_c
    };
}

export function bindMessage (message) {
    Store.message = message;
}

export function sheetNamePreCheck (name, order) {
    // 检测是否重名
    for (let i = 0; i < Store.luckysheetfile.length; i++) {
        if (order != i && Store.luckysheetfile[i].name == name) {
            const _locale = locale();
            const locale_sheetconfig = _locale.sheetconfig;
            return {
                res: false,
                msg: locale_sheetconfig.tipNameRepeat
            };
        }
    }
    return {
        res: true
    };
}

export function buildGridData (file) {
    return sheetmanage.buildGridData(file);
}

export function setCanDropHighLight (canDrop) {
    Store.canDropHighLight = canDrop;
    if (!canDrop)  clearHighLightArea('drag');
}

export function dragHoverEvent (row_index, col_index, color) {
    clearHighLightArea('drag');
    if (Store.canDropHighLight) {
        setHighLightArea([{
            row: [row_index, row_index],
            column: [col_index, col_index]
        }], {
            type: 'mask',
            color,
            use: 'drag'
        });
        return {
            row: row_index,
            column: col_index
        };
    }
    return null;
}

export function axisToPosition (pageX, pageY) {
    let mouse = mouseposition(pageX, pageY);
    let x = mouse[0] + $("#luckysheet-cell-main").scrollLeft();
    let y = mouse[1] + $("#luckysheet-cell-main").scrollTop();
    let row_location = rowLocation(y),
        row_index = row_location[2];
    let col_location = colLocation(x),
        col_index = col_location[2];
    if (x > 0 && y > 0) {
        return {
            row: row_index,
            column: col_index
        };
    }
    return null;
}

/**
 * 给 sheet 设置数据并刷新
 * @param {*} data
 * @param {*} sheetIndex
 */
export function setSheetData (newData, sheetIndex) {
    // 隐藏 操作导致显示的dom节点
    const data = newData;
    Store.luckysheet_selection_range = [];
    // formula.cancelNormalSelected();
    $("#luckysheet-rich-text-editor").html('');
    clearActiveArea(false);
    // 重置编辑单元格数据
    const oldSheetIndex = Store.currentSheetIndex;
    Store.luckysheetCellUpdate = [];
    data.celldata.forEach((cell) => {
        if (cell.v && cell.v.v && isString(cell.v.v) && cell.v.v.includes('\r\n')) {
            if (cell.v.ct && cell.v.ct.s && cell.v.ct.s.length) {
                if (cell.v.v !== cell.v.ct.s.map((cts) => cts.v).join('')) {
                    cell.v.ct.s = [
                        {
                            "v": cell.v.v
                        }
                    ];
                }
            } else {
                cell.v.ct = {
                    "fa": "General",
                    "t": "inlineStr",
                    "s": [
                        {
                            "v": cell.v.v
                        }
                    ]
                };
            }

            // delete cell.v.v;
        }
    });
    let loadCelldata = data.celldata.filter((c) => +c.r < Store.defaultrowNum);
    if (!Store.luckysheetfile[sheetIndex]) {
        sheetmanage.addNewSheet(null, null, true);
        if (data.name)setSheetName(data.name, { order: sheetIndex });
    };
    setSheetEdit(sheetIndex, data.edit);
    const celldata = [];
    const calcChain = [];
    loadCelldata.forEach((cell) => {
        if (cell && cell.v) {
            let vm = {};
            if (cell.v.v === '#d') {
                vm.v = '#待查询';
                vm.m = '';
            };
            if (cell.v.isInsert) delete cell.v.isInsert;
            const ns = nameStyleConvert(cell.v.ns);
            for (let key of Object.keys(ns)) {
                cell.v[key] = ns[key];
            }
            for (let key of Object.keys(vm)) {
                cell.v[key] = vm[key];
            }
        }
        celldata.push(cell);
        if (cell && cell.v && cell.v.f) {
            calcChain.push({
                r: cell.r,
                c: cell.c,
                index: Store.luckysheetfile[sheetIndex].index
            });
        }
    });
    loadCelldata = celldata;
    data.calcChain = calcChain;
    let da = { celldata: loadCelldata, config: data.config, calcChain: data.calcChain, edit: data.edit };
    Store.loadDataCount[sheetIndex] = Store.defaultrowNum;
    Store.luckysheetfile[sheetIndex] = { ...Store.luckysheetfile[sheetIndex], ...da };
    // Store.config = $.extend(true, {}, data.config);
    Store.luckysheetfile[sheetIndex].oldRow = Store.luckysheetfile[sheetIndex].data.length;
    Store.luckysheetfile[sheetIndex].oldColumn = Store.luckysheetfile[sheetIndex].data[0].length;
    Store.luckysheetfile[sheetIndex].data = [];
    Store.luckysheetfile[sheetIndex].load = null;
    const maxRowLen = getMaxRowLen(Store.luckysheetfile[sheetIndex].celldata);
    const maxColumnLen = getMaxColumnLen(Store.luckysheetfile[sheetIndex].celldata);
    Store.luckysheetfile[sheetIndex].row =  maxRowLen > 100 ? maxRowLen : 100;
    Store.luckysheetfile[sheetIndex].column = maxColumnLen > 26 ? maxColumnLen : 26;
    Store.luckysheetfile[sheetIndex].preRow = data.preRow ? data.preRow : Store.luckysheetfile[sheetIndex].row;
    Store.luckysheetfile[sheetIndex].preColumn = data.preColumn ? data.preColumn : Store.luckysheetfile[sheetIndex].column;
    Store.luckysheetfile[sheetIndex].filter_select = data.filter_select;
    Store.luckysheetfile[sheetIndex].filter = {};
    Store.luckysheetfile[sheetIndex].luckysheet_conditionformat_save = data.luckysheet_conditionformat_save;
    Store.luckysheetfile[sheetIndex].dataVerification = data.dataVerification;
    Store.luckysheetfile[sheetIndex].computeMap = null;
    Store.flowdata = [];
    $("#luckysheet-filter-selected-sheet" + Store.luckysheetfile[sheetIndex].index).remove();
    $("#luckysheet-filter-options-sheet" + Store.luckysheetfile[sheetIndex].index).remove();
    sheetmanage.reRenderSheet(Store.luckysheetfile[sheetIndex].index);
    sheetmanage.changeSheet(oldSheetIndex, null, null, null, true);
}

export function setSheetsData (sheetsDataArray, cb) {
    Store.luckysheet_selection_range = [];
    // formula.cancelNormalSelected();
    $("#luckysheet-rich-text-editor").html('');
    clearActiveArea(false);
    // 重置编辑单元格数据
    const oldSheetIndex = Store.currentSheetIndex;
    Store.luckysheetCellUpdate = [];
    for (let sheetDataItem of sheetsDataArray) {
        const { sheetData, sheetIndex } = sheetDataItem;
        const data = sheetData;
        data.celldata.forEach((cell) => {
            if (cell.v && cell.v.v && isString(cell.v.v) && cell.v.v.includes('\r\n')) {
                if (cell.v.ct && cell.v.ct.s && cell.v.ct.s.length) {
                    if (cell.v.v !== cell.v.ct.s.map((cts) => cts.v).join('')) {
                        cell.v.ct.s = [
                            {
                                "v": cell.v.v
                            }
                        ];
                    }
                } else {
                    cell.v.ct = {
                        "fa": "General",
                        "t": "inlineStr",
                        "s": [
                            {
                                "v": cell.v.v
                            }
                        ]
                    };
                }
                // delete cell.v.v;
            }
        });
        let loadCelldata = data.celldata.filter((c) => +c.r < Store.defaultrowNum);
        if (!Store.luckysheetfile[sheetIndex]) {
            sheetmanage.addNewSheet(null, null, true);
            if (data.name)setSheetName(data.name, { order: sheetIndex });
        };
        setSheetEdit(sheetIndex, data.edit);
        const celldata = [];
        const calcChain = [];
        loadCelldata.forEach((cell) => {
            if (cell && cell.v) {
                let vm = {};
                if (cell.v.v === '#d') {
                    vm.v = '#待查询';
                    vm.m = '';
                };
                if (cell.v.isInsert) delete cell.v.isInsert;
                const ns = nameStyleConvert(cell.v.ns);
                for (let key of Object.keys(ns)) {
                    cell.v[key] = ns[key];
                }
                for (let key of Object.keys(vm)) {
                    cell.v[key] = vm[key];
                }
            }
            celldata.push(cell);
            if (cell && cell.v && cell.v.f) {
                calcChain.push({
                    r: cell.r,
                    c: cell.c,
                    index: Store.luckysheetfile[sheetIndex].index
                });
            }
        });
        loadCelldata = celldata;
        data.calcChain = calcChain;
        let da = { celldata: loadCelldata, config: data.config, calcChain: data.calcChain, edit: data.edit };
        Store.loadDataCount[sheetIndex] = Store.defaultrowNum;
        Store.luckysheetfile[sheetIndex] = { ...Store.luckysheetfile[sheetIndex], ...da };
        // Store.config = $.extend(true, {}, data.config);
        Store.luckysheetfile[sheetIndex].oldRow = Store.luckysheetfile[sheetIndex].data.length;
        Store.luckysheetfile[sheetIndex].oldColumn = Store.luckysheetfile[sheetIndex].data[0].length;
        Store.luckysheetfile[sheetIndex].data = [];
        Store.luckysheetfile[sheetIndex].data = sheetmanage.buildGridData(Store.luckysheetfile[sheetIndex], false, false);
        Store.luckysheetfile[sheetIndex].load = null;
        const maxRowLen = getMaxRowLen(Store.luckysheetfile[sheetIndex].celldata);
        const maxColumnLen = getMaxColumnLen(Store.luckysheetfile[sheetIndex].celldata);
        Store.luckysheetfile[sheetIndex].row =  maxRowLen > 100 ? maxRowLen : 100;
        Store.luckysheetfile[sheetIndex].column = maxColumnLen > 26 ? maxColumnLen : 26;
        Store.luckysheetfile[sheetIndex].preRow = data.preRow ? data.preRow : Store.luckysheetfile[sheetIndex].row;
        Store.luckysheetfile[sheetIndex].preColumn = data.preColumn ? data.preColumn : Store.luckysheetfile[sheetIndex].column;
        Store.luckysheetfile[sheetIndex].filter_select = data.filter_select;
        Store.luckysheetfile[sheetIndex].filter = {};
        Store.luckysheetfile[sheetIndex].luckysheet_conditionformat_save = data.luckysheet_conditionformat_save;
        Store.luckysheetfile[sheetIndex].dataVerification = data.dataVerification;
        Store.luckysheetfile[sheetIndex].computeMap = null;
        $("#luckysheet-filter-selected-sheet" + Store.luckysheetfile[sheetIndex].index).remove();
        $("#luckysheet-filter-options-sheet" + Store.luckysheetfile[sheetIndex].index).remove();
    }
    Store.flowdata = [];
    sheetmanage.reRenderSheet(oldSheetIndex, null, cb);
    sheetmanage.changeSheet(oldSheetIndex, null, null, null, true);
}

export function setCellAttribute (r, c, attr, options) {
    const {
        order = getCurrentSheetIndex(), rerender = true
    } = options;
    const file = Store.luckysheetfile[order];
    let data = file.data;
    if (data.length == 0) {
        data = sheetmanage.buildGridData(file);
    }
    if (data[r]) {
        let cellData = { ...(data[r][c] || {}) };
        if (cellData && cellData.ct && cellData.ct.t === 'inlineStr') {
            Object.keys(attr).forEach((a) => {
                if (a in inlineStyleAffectAttribute) {
                    updateInlineStringFormatOutside(cellData, a, attr[a]);
                }
            });
        }
        data[r][c] = { ...cellData, ...attr };
    }
    if (file.index == Store.currentSheetIndex) {
        if (data[r] && data[r][c] && data[r][c].v && data[r][c].ct && !data[r][c].editRender && !data[r][c].ct.t === 'inlineStr') {
            data[r][c].m = update(data[r][c].ct.fa, data[r][c].v);
        }
        Store.flowdata = data;
        if (rerender && !Store.suspend) jfrefreshgrid(data, [{ "row": [r, r], "column": [c, c] }], null, false);
    } else {
        file.data = data;
    }
}

export function deleteCellAttribute (r, c, attrKeys, options = {}) {
    const {
        order = getCurrentSheetIndex(), rerender = true
    } = options;
    const file = Store.luckysheetfile[order];
    let data = file.data;
    if (data.length == 0) {
        data = sheetmanage.buildGridData(file);
    }
    if (data[r] && data[r][c]) {
        const oldValue = cloneDeep(data[r][c]);
        attrKeys.forEach((key) => {
            delete data[r][c][key];
            if (key === 'ct') {
                let mask = genarate(data[r][c].v);
                if (mask && mask.length) {
                    data[r][c].ct = mask[1];
                    data[r][c].m = mask[0];
                }
            }
            if (key === 'f') {
                file.calcChain = file.calcChain.filter((cal) => {
                    if (cal.r === r && cal.c === c && cal.index === order) return false;
                    return true;
                });
                formula.execFunctionGroupForce(true);
                formula.groupValuesRefresh();
            }
        });
        if (attrKeys.some((k) => k === 'v' || k === 'f' || k === 'm')) {
            if (data[r][c].ct && data[r][c].ct.t === 'inlineStr') {
                delete data[r][c].ct;
            }
            if (oldValue && oldValue.ct &&  oldValue.ct.t === 'inlineStr') {
                oldValue.m = oldValue.v;
            }
            let updateValue = data[r][c]
                ? {
                    ...data[r][c]
                }
                : null;
            if (updateValue && updateValue.ct && updateValue.ct.t === 'inlineStr') {
                updateValue.m = updateValue.v;
            }
            method.createHookFunction("cellUpdated", r, c, oldValue, updateValue, order);
        }
    }
    if (file.index == Store.currentSheetIndex) {
        Store.flowdata = data;
        if (rerender && !Store.suspend) jfrefreshgrid(data, [{ "row": [r, r], "column": [c, c] }], null, false);
    } else {
        file.data = data;
    }
}

/**
 * 设置范围单元格属性
 * @param {*} range { row: [0, 1], column: [0, 2] }
 * @param {*} attr   ｛ edit: true ｝
 * @param {*} sheetIndex
 */
export function setRangeAttributes (range, attr, sheetIndex = getCurrentSheetIndex()) {
    try {
        const file = Store.luckysheetfile[sheetIndex];
        let data = file.data;
        if (data.length == 0) {
            data = sheetmanage.buildGridData(file);
        }
        const { row, column } = range;
        for (let i = row[0]; i <= row[1]; i++) {
            for (let j = column[0]; j <= column[1]; j++) {
                if (data[i] && data[i][j]) {
                    let cellData = { ...data[i][j] };
                    if (cellData && cellData.ct && cellData.ct.t === 'inlineStr') {
                        Object.keys(attr).forEach((a) => {
                            if (a in inlineStyleAffectAttribute) {
                                updateInlineStringFormatOutside(cellData, a, attr[a]);
                            }
                        });
                    }
                    data[i][j] = { ...cellData, ...attr };
                } else if (data[i]) {
                    data[i][j] = { ...attr };
                }
            }
        }
        if (file.index == Store.currentSheetIndex) {
            jfrefreshgrid(data, [{ "row": row, "column": column }], null, false);
        } else {
            file.data = data;// only update data
        }
    } catch (e) {
        console.log(e, range, attr);
    }
}

/**
 * 传入所有sheet的rangeList
* @param {*} rangesInfo { sheetIndex: [{ row: [0, 1], column: [0, 1] }, { row: [1, 2], column: [1, 2] }] }
 * @returns sheetInfoList
 */
export function getAllSheetInfoByRanges (rangesInfo) {
    const res = {};
    const sheetIndexList = Object.keys(rangesInfo);
    sheetIndexList.forEach((sheetIndex) => {
        const sheetRangeList = rangesInfo[sheetIndex];
        const celldata = [];
        const file = Store.luckysheetfile[sheetIndex];
        const { row, column, calcChain } = file;
        if (!file.data || file.data.length === 0) {
            file.data = sheetmanage.buildGridData(file);
        }
        let config = jsondiffpatch.clone(file.config);
        config.merge = config.merge || {};
        const merge = config.merge;
        let deleteKey = [];
        const keys = Object.keys(merge);
        const filterKeys = keys.filter((key) => {
            let mergeInArea = false;
            const { r, c, rs, cs } = merge[key];
            sheetRangeList.forEach((range) => {
                const { row, column } = range;
                if (r >= row[0] && r <= row[1] && c >= column[0] && c <= column[1]) mergeInArea = mergeInArea || true;
            });
            return !mergeInArea;
        });
        for (let i = 0; i < filterKeys.length; i++) {
            for (let j = 0; j < filterKeys.length; j++) {
                if (i === j) continue;
                if (deleteKey.includes(filterKeys[i]) || deleteKey.includes(filterKeys[j])) continue;
                const { r, c, rs, cs } = merge[filterKeys[i]];
                const { r: r1, c: c1, rs: rs1, cs: cs1 } = merge[filterKeys[j]];
                if (doRectanglesOverlap(merge[filterKeys[i]], merge[filterKeys[j]])) {
                    const count = rs * cs;
                    const count1 = rs1 * cs1;
                    if (count >= count1) deleteKey.push(filterKeys[j]);
                    else {
                        deleteKey.push(filterKeys[i]);
                    }
                }
            }
        }
        Object.keys(config.merge).forEach((key) => {
            const mergeItem = config.merge[key];
            const { r, c, rs, cs } = mergeItem;
            for (let r1 = r; r1 < r + rs; r1++) {
                for (let c1 = c; c1 < c + cs; c1++) {
                    if (file.data[r1][c1]) {
                        if (file.data[r1][c1].mc && file.data[r1][c1].mc.r === r && file.data[r1][c1].mc.c === c) continue;
                        else {
                            file.data[r1][c1].mc = {
                                r,
                                c
                            };
                        }
                    }
                }
            }
        });
        deleteKey.forEach((key) => delete config.merge[key]);
        for (let r = 0; r < file.data.length; r++) {
            for (let c = 0; c < file.data[0].length; c++) {
                if (file.data[r][c] === null) continue;
                let inArea = false;
                sheetRangeList.forEach((range) => {
                    const { row, column } = range;
                    if (r >= row[0] && r <= row[1] && c >= column[0] && c <= column[1]) inArea = inArea || true;
                });
                if (!inArea && file.data[r][c].mc) {
                    if (!config.merge[`${file.data[r][c].mc.r}_${file.data[r][c].mc.c}`]) delete file.data[r][c].mc;
                }
                const v = jsondiffpatch.clone(file.data[r][c]);
                const bg = (v.bg || '').toLowerCase();
                const fc = (v.fc || '').toLowerCase();
                delete v.of;
                delete v.ov;
                delete v.ns;
                if (
                    (bg === '#fff' || bg === '') &&
                    (fc === '#000' || fc === '#000000' || fc === '') &&
                    (v.fs < 0 || v.fs == 10) &&
                    (v.bl < 0 || v.bl == 0) &&
                    (v.cl < 0 || v.cl == 0) &&
                    (v.ht < 0 || v.ht == 1) &&
                    (v.it < 0 || v.it == 0) &&
                    (v.un < 0 || v.un == 0) &&
                    (v.tb < 0 || v.tb == 1) &&
                    (v.vt < 0 || v.vt == 0)
                ) {
                    delete v.bg;
                    delete v.fc;
                    delete v.fs;
                    delete v.bl;
                    delete v.cl;
                    delete v.ht;
                    delete v.it;
                    delete v.un;
                    delete v.tb;
                    delete v.vt;
                } else {
                    let nameStyle = '';
                    let useNameStyle = true;
                    /// bg 2位 fc 2位 fs 2位 bl 1位 cl 1位 ht 1位 it 1位 un 1位 tb 1位 vt 1位
                    if (hex2OrderMap[bg]) {
                        nameStyle += hex2OrderMap[bg];
                    } else {
                        useNameStyle = false;
                    }
                    if (hex2OrderMap[fc] && useNameStyle) {
                        nameStyle += hex2OrderMap[fc];
                    } else {
                        useNameStyle = false;
                    }
                    if (useNameStyle) {
                        nameStyle += !isNil(v.fs) ? v.fs < 10 ? `0${v.fs}` : v.fs : 10;
                        nameStyle += !isNil(v.bl) ? v.bl : 0;
                        nameStyle += !isNil(v.cl) ? v.cl : 0;
                        nameStyle += !isNil(v.ht) ? v.ht : 1;
                        nameStyle += !isNil(v.it) ? v.it : 0;
                        nameStyle += !isNil(v.un) ? v.un : 0;
                        nameStyle += !isNil(v.tb) ? v.tb : 1;
                        nameStyle += !isNil(v.vt) ? v.vt : 0;
                        delete v.bg;
                        delete v.fc;
                        delete v.fs;
                        delete v.bl;
                        delete v.cl;
                        delete v.ht;
                        delete v.it;
                        delete v.un;
                        delete v.tb;
                        delete v.vt;
                        v.ns = nameStyle;
                    }
                }
                const vm = v.m === '#待查询' ? '#d' : v.m;
                let vv = v.v === '#待查询' ? '#d' : v.v;
                if (v.ct && v.ct.s && v.ct.s.length) vv = v.ct.s.map((vi) => vi.v).join('');
                if (!inArea && v) celldata.push({ r: r, c: c, v: { ...v, v: vv, m: vm } });
            }
        }
        res[sheetIndex] = {
            row: file.row,
            column: file.column,
            config,
            celldata,
            finalRow: file.data.length,
            finalColumn: file.data[0] ? file.data[0].length : 0,
            color: file.color
            // calcChain
        };
    });
    return res;
}

export function activeFormulaDialog () {
    let last = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1];

    let row_index = last.row_focus, col_index = last.column_focus;

    luckysheetupdateCell(row_index, col_index, Store.flowdata);
    let cell = Store.flowdata[row_index][col_index];
    if (cell != null && cell.f != null) {
        // 单元格有计算
        let functionStr = formula.getfunctionParam(cell.f);
        if (functionStr.fn != null) {
            // 有函数公式
            insertFormula.formulaParmDialog(functionStr.fn, functionStr.param);
        } else {
            // 无函数公式
            insertFormula.formulaListDialog();
        }
    } else {
        // 单元格无计算
        $("#luckysheet-rich-text-editor").html('<span dir="auto" class="luckysheet-formula-text-color">=</span>');
        $("#luckysheet-functionbox-cell").html($("#luckysheet-rich-text-editor").html());
        insertFormula.formulaListDialog();
    }
    Store.activeFormula = true;
    insertFormula.init();
}

// 单元格设置公式
export function setCellFormula (funcText, attr = {}, r, c, updateCell = true) {
    let last = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1];
    let row_index = last?.row_focus || 0, col_index = last?.column_focus || 0;
    if (r !== undefined && c !== undefined) {
        row_index = r;
        col_index = c;
    }

    const file = Store.luckysheetfile[getCurrentSheetIndex()];
    if (file.data[row_index] && file.data[row_index][col_index]) {
        file.data[row_index][col_index] = { ...file.data[row_index][col_index], ...attr };
    } else if (file.data[row_index]) {
        file.data[row_index][col_index] = { ...attr };
    }
    formula.groupValuesRefreshData = formula.groupValuesRefreshData.filter((item) => item.r != r && item.c != c);
    Store.flowdata[r][c].f = funcText;
    let v = formula.execfunction(funcText, row_index, col_index);
    Store.flowdata[r][c].v = v[1];
    if (Store.flowdata[r][c].ct != null && Store.flowdata[r][c].ct.fa != null) {
        Store.flowdata[r][c].m = update(Store.flowdata[r][c].ct.fa, v[1]);
    }
    if (updateCell) {
        formula.updatecell(row_index, col_index, funcText, true, true, true);
    } else {
        formula.execFunctionExist = formula.execFunctionExist || [];
        formula.execFunctionExist.push({ r, c, i: Store.currentSheetIndex });
    }
}

export function exportExcelExcludeRanges (rangesInfo, name, param) {
    const res = [];
    const sheetIndexList = Object.keys(rangesInfo);
    sheetIndexList.forEach((sheetIndex) => {
        const sheetRangeList = rangesInfo[sheetIndex];
        const file = Store.luckysheetfile[sheetIndex];
        if (!file.data || file.data.length === 0) {
            file.data = sheetmanage.buildGridData(file);
        }
        res[sheetIndex] = deepCopy(file);
        if (res[sheetIndex].config?.borderInfo) {
            const borderList = res[sheetIndex].config.borderInfo.filter((border) => {
                let inArea = false;
                if (border.rangeType === 'range') {
                    sheetRangeList.forEach((range) => {
                        border.range.forEach((bRange) => {
                            const { row, column } = range;
                            const { row: bRow, column: bCloumn } = bRange;
                            if (bRow[0] >= row[0] && bRow[1] <= row[1] && bCloumn[0] >= column[0] && bCloumn[1] <= column[1]) inArea = true;
                        });
                    });
                }
                return !inArea;
            });
            res[sheetIndex].config.borderInfo = borderList;
        }
        if (res[sheetIndex].config?.merge) {
            const mergeInfoList = Object.keys(res[sheetIndex].config.merge).map((mr) => mr.split('_')).filter((mrInfo) => {
                let inArea = false;
                sheetRangeList.forEach((range) => {
                    const { row, column } = range;
                    if (mrInfo[0] >= row[0] && mrInfo[0] <= row[1] && mrInfo[1] >= column[0] && mrInfo[1] <= column[1]) inArea = true;
                });
                return !inArea;
            });
            let merge = {};
            mergeInfoList.forEach((mrInfo) => {
                merge[`${mrInfo[0]}_${mrInfo[1]}`] = res[sheetIndex].config.merge[`${mrInfo[0]}_${mrInfo[1]}`];
            });
            res[sheetIndex].config.merge = merge;
        }
        for (let r = 0; r < file.data.length; r++) {
            for (let c = 0; c < file.data[0].length; c++) {
                if (file.data[r][c] === null) continue;
                sheetRangeList.forEach((range) => {
                    const { row, column } = range;
                    if (r >= row[0] && r <= row[1] && c >= column[0] && c <= column[1]) res[sheetIndex].data[r][c] = null;
                });
            }
        }
    });
    return exportExcelFromJson(res, name, param);
}

export function removeRangeBorder (rangeList, sheetIndex = getSheetIndex(Store.currentSheetIndex)) {
    const file = Store.luckysheetfile[sheetIndex];
    for (let range of rangeList) {
        const { row, column } = range;
        let start_r = row[0];
        let start_c = column[0];
        let end_r = row[1];
        let end_c = column[1];
        if (file.config.borderInfo && file.config.borderInfo.length) {
            file.config.borderInfo = file.config.borderInfo.filter((border) => {
                if (border.rangeType === 'range') {
                    let inRange = false;
                    border.range.forEach((r) => {
                        if (r.row[0] >= start_r && r.row[1] <= end_r && r.column[0] >= start_c && r.column[1] <= end_c) inRange = true;
                    });
                    return !inRange;
                }
                return true;
            });
        }
    }
    // 若操作为当前sheet页，则刷新当前sheet页
    if (sheetIndex === getSheetIndex(Store.currentSheetIndex)) {
        jfrefreshgrid(file.data, rangeList);
    } else {
        Store.luckysheetfile[sheetIndex] = file;
    }
}

function deepMerge (target, source) {
    // 遍历source对象的所有属性
    for (const key in source) {
        // 如果source中的属性是对象，且target中也有这个属性且为对象
        if (source[key] instanceof Object && key in target) {
            // 递归合并这两个对象
            target[key] = deepMerge(Object.assign({}, target[key]), source[key]);
        } else {
            // 否则，直接覆盖或添加属性
            target[key] = source[key];
        }
    }
    return target;
}

export function batchSetAttribute (cellList, attribute, value, options = {}) {
    let curOrder = getSheetIndex(Store.currentSheetIndex);
    const {
        rerender = true,
        funcCalculate = false,
        order = curOrder
    } = options;
    let data = Store.luckysheetfile[order].data;
    for (let rc of cellList) {
        const { r, c } = rc;
        if (data[r] && data[r][c]) {
            if (attribute) {
                if (attribute === 'tag') {
                    data[r][c][attribute] = deepMerge(data[r][c][attribute] || {}, value);
                } else {
                    data[r][c][attribute] = value;
                }
            }
            if (attribute === 'ct') {
                if (value && value.fa) {
                    let mask = update(value.fa, data[r][c].v);
                    if (mask !== value.v) {
                        data[r][c].m = mask.toString();
                    } else {
                        data[r][c].m = value.v;
                    }
                }
            }
        }
    }

    if (rerender && curOrder === order) {
        jfrefreshgrid(data, [{ "row": [0, Store.flowdata[0]], "column": [0, Store.flowdata[0].length] }], null, funcCalculate, true);
    }
}

export function batchDeleteAttribute (cellList, attrKeys = [], options = {}) {
    let curOrder = getSheetIndex(Store.currentSheetIndex);
    const {
        rerender = true,
        funcCalculate = false,
        order = curOrder
    } = options;
    let file = Store.luckysheetfile[order];
    let data = Store.luckysheetfile[order].data;
    for (let rc of cellList) {
        const { r, c } = rc;
        if (data[r] && data[r][c]) {
            const oldValue = cloneDeep(data[r][c]);
            attrKeys.forEach((key) => {
                delete data[r][c][key];
                if (key === 'ct') {
                    let mask = genarate(data[r][c].v);
                    if (mask && mask.length) {
                        data[r][c].ct = mask[1];
                        data[r][c].m = mask[0];
                    }
                }
                if (key === 'f') {
                    file.calcChain = file.calcChain.filter((cal) => {
                        if (cal.r === r && cal.c === c && cal.index === order) return false;
                        return true;
                    });
                    formula.execFunctionGroupForce(true);
                    formula.groupValuesRefresh();
                }
            });
            if (attrKeys.some((k) => k === 'v' || k === 'f' || k === 'm')) {
                if (data[r][c].ct && data[r][c].ct.t === 'inlineStr') {
                    delete data[r][c].ct;
                }
                if (oldValue && oldValue.ct &&  oldValue.ct.t === 'inlineStr') {
                    oldValue.m = oldValue.v;
                }
                let updateValue = data[r][c]
                    ? {
                        ...data[r][c]
                    }
                    : null;
                if (updateValue && updateValue.ct && updateValue.ct.t === 'inlineStr') {
                    updateValue.m = updateValue.v;
                }
                method.createHookFunction("cellUpdated", r, c, oldValue, updateValue, order);
            }
        }
    }

    if (rerender && curOrder === order) {
        jfrefreshgrid(data, [{ "row": [0, Store.flowdata[0]], "column": [0, Store.flowdata[0].length] }], null, funcCalculate, true);
    }
}

export function transformData2OriginData (data) {
    return jsondiffpatch.clone(data);
}

export function initOriginData (callback) {
    // 调用初始化后还会执行计算公式逻辑   所以将该过程滞后 保证是最新数据
    setTimeout(() => {
        Store.luckysheetfile.forEach((file) => {
            if (!file.data || file.data.length === 0) {
                file.data = sheetmanage.buildGridData(file, false, false);
                if (file.data.length) {
                    rhchInit(file.data.length, file.data[0].length, getSheetIndex(file.index), false);
                }
            }
        });
        formula.execFunctionGroupForce(luckysheetConfigsetting.forceCalculation);
        formula.groupValuesRefresh();
        Store.luckysheetfile.forEach((file) => {
            createCopyValueAndFormula(file.data);
        });
        luckysheetrefreshgrid();
        if (typeof callback === 'function') {
            callback();
        }
    }, 200);
}

function createCopyValueAndFormula (data) {
    data.forEach((rowData) => {
        rowData.forEach((cell) => {
            if (cell) {
                if (!isNil(cell.v))cell.ov = cell.v;
                if (cell.ct && cell.ct.s && cell.ct.s.length) {
                    let concatV = cell.ct.s.map((s) => s.v).join('');
                    cell.ov = concatV;
                }
                if (!isNil(cell.f)) cell.of = cell.f;
            }
        });
    });
}

export function getReallySize (sheetOrder = getCurrentSheetIndex()) {
    if (sheetOrder == getCurrentSheetIndex()) {
        return {
            row: Store.flowdata.length,
            col: Store.flowdata[0].length
        };
    } else {
        let file = Store.luckysheetfile[sheetOrder];
        if (file.data && file.data.length > 0) {
            return {
                row: file.data.length,
                col: file.data[0].length
            };
        } else {
            return {
                row: file.row,
                col: file.column
            };
        }
    }
}

export function setFormulaBarShow (show = true) {
    luckysheetConfigsetting.sheetFormulaBar = show;
    luckysheetsizeauto();
}

export function setRowNumColNumShow (show = true) {
    if (show) {
        luckysheetConfigsetting.rowHeaderWidth = 46;
        luckysheetConfigsetting.columnHeaderHeight = 20;
    } else {
        luckysheetConfigsetting.rowHeaderWidth = 0;
        luckysheetConfigsetting.columnHeaderHeight = 0;
    }
    zoomSetting();
    luckysheetsizeauto();
}

/**
 * 显示指定下标工作表的网格线，返回操作的工作表对象
*/
export function setGridLinesShow (orderList = []) {
    Store.luckysheetfile.forEach((file) => file.showGridLines = true);
    orderList.forEach((order) => {
        let file = Store.luckysheetfile[order];
        if (file != null) {
            file.showGridLines = false;
        }
    });
    let curFile = Store.luckysheetfile[getCurrentSheetIndex()];
    Store.showGridLines = curFile.showGridLines;
    setTimeout(function () {
        luckysheetrefreshgrid();
    }, 1);
}

export function isCellData (value) {
    return formula.iscelldata(value);
}

export function isCellDataSingle (value) {
    return formula.iscelldatasingle(value);
}

export function isCellDataRange (value) {
    return formula.iscelldatarange(value);
}

export function suspendDraw (isSuspend = true) {
    Store.suspend = isSuspend;
    if (!isSuspend) {
        Store.luckysheetfile[getCurrentSheetIndex()].computeMap = null;
        luckysheetrefreshgrid();
    }
}

export function suspendCalculate (isSuspend = true) {
    Store.suspendCalculate = isSuspend;
    if (!isSuspend) {
        formula.execFunctionExist = formula.execFunctionExist || [];
        formula.suspendCellList.forEach((cell) => {
            formula.execFunctionExist.push({ "r": cell.r, "c": cell.c, "i": cell.i });
        });
        formula.execFunctionExist.reverse();
        formula.execFunctionGroup();
        formula.execFunctionGlobalData = null;
        // formula.groupValuesRefresh();
        luckysheetrefreshgrid();
        setTimeout(() => {
            formula.suspendCellList = [];
            formula.execFunctionExist = null;
        });
    }
}

export function syncOriginData (flag, ignoreChain = false) {
    Store.syncOrigin.flag = flag;
    Store.syncOrigin.ignoreChain = ignoreChain;
}

export function getRowDataCount (sheetIndex) {
    let index =  method.getInputSheetIndex(sheetIndex);
    let file = Store.luckysheetfile[index];
    let rowLen;
    if (file.data) {
        rowLen = file.data.length;
    }
    const sheet = sheetmanage.getSheetByIndex(file.index);
    if (!rowLen) rowLen = getMaxRowLen(sheet.celldata);
    return rowLen;
}

export function getColumnDataCount (sheetIndex) {
    let index =  method.getInputSheetIndex(sheetIndex);
    let file = Store.luckysheetfile[index];
    let columnLen;
    if (file.data && file.data[0]) {
        columnLen = file.data[0].length;
    }
    const sheet = sheetmanage.getSheetByIndex(file.index);
    if (!columnLen) columnLen = getMaxColumnLen(sheet.celldata);
    return columnLen;
}

export function functionCopy (txt, mode, step) {
    return formula.functionCopy(txt, mode, step);
}

export function addConditionFormat (conditionformat, order) {
    let file = Store.luckysheetfile[order];
    file.luckysheet_conditionformat_save = file.luckysheet_conditionformat_save || [];
    file.luckysheet_conditionformat_save.push(conditionformat);
    file.computeMap = null;
    if (order === getCurrentSheetIndex()) {
        setTimeout(function () {
            luckysheetrefreshgrid();
        }, 1);
    }
}

export function addDataVerify (dataverifyObj, order) {
    let file = Store.luckysheetfile[order];
    file.dataVerification = file.dataVerification || {};
    file.dataVerification = { ...file.dataVerification, ...dataverifyObj };
    if (order === getCurrentSheetIndex()) {
        dataVerificationCtrl.dataVerification = file.dataVerification;
        setTimeout(function () {
            luckysheetrefreshgrid();
        }, 1);
    }
}

export function addCustomCellRightClick (title, onClick, verify) {
    const dom = `<div data-index="${luckysheetConfigsetting.cellRightClickConfig.customs.length}" id="luckysheetColsRowsHandleAdd_custom_${luckysheetConfigsetting.cellRightClickConfig.customs.length}" class="luckysheetColsRowsHandleAdd_custom luckysheet-cols-menuitem luckysheet-mousedown-cancel">
        <div class="luckysheet-cols-menuitem-content luckysheet-mousedown-cancel">
        ${title}
        </div>
    </div>`;
    if (luckysheetConfigsetting.cellRightClickConfig.customs.length) {
        $(`#luckysheetColsRowsHandleAdd_custom_${luckysheetConfigsetting.cellRightClickConfig.customs.length - 1}`).after($(dom));
    } else {
        $(`#luckysheetCellFormatRightClickMenu`).after($(dom));
    }
    luckysheetConfigsetting.cellRightClickConfig.customs.push({ title, onClick, verify });
    $(".luckysheetColsRowsHandleAdd_custom").off("click").click(function (clickEvent) {
        $("#luckysheet-rightclick-menu").hide();
        const cellRightClickConfig =
            luckysheetConfigsetting.cellRightClickConfig;
        const rowIndex = Store.luckysheet_select_save[0].row[0];
        const columnIndex = Store.luckysheet_select_save[0].column[0];
        if (
            cellRightClickConfig.customs[
                Number(clickEvent.currentTarget.dataset.index)
            ]
        ) {
            try {
                cellRightClickConfig.customs[
                    Number(clickEvent.currentTarget.dataset.index)
                ].onClick({ rowIndex, columnIndex, data: Store.flowdata[rowIndex][columnIndex], rightClickTarget: Store.rightClickPosition });
            } catch (e) {
            }
        }
    });
};

export function setSheetEdit (sheetIndex, edit = true) {
    const file = Store.luckysheetfile[sheetIndex];
    file.edit = edit;
    if (edit === false) {
        $(`#luckysheet-sheets-item-lock-${file.index}`).show();
    } else {
        $(`#luckysheet-sheets-item-lock-${file.index}`).hide();
    }
};

export function setFilterConditions (sheetIndex, column, condition) {
    filterByCondition(sheetIndex, column, condition);
}

export function getDPI () {
    return getDpi();
}

export function translateRegion (range, direction, offset, order) {
    let file = Store.luckysheetfile[order];
    let data = file.data;
    // 创建一个副本以保存偏移后的矩阵
    const merge = file.config.merge || {};
    const { row, column } = range;
    let deleteMerge = [];
    Object.keys(merge).forEach((mergeKey) => {
        const item = merge[mergeKey];
        // 不在区域里的全部清空
        if (doRectanglesOverlap(item, {
            r: row[0],
            c: column[0],
            rs: row[1] - row[0] + 1,
            cs: column[1] - column[0] + 1
        }) && !((item.r >= row[0] && (item.r + item.rs - 1) <= row[1]) && (item.c >= column[0] && (item.c + item.cs - 1) <= column[1]))) {
            deleteMerge.push(mergeKey);
        }
    });
    deleteMerge.forEach((key) => {
        const mergeItem = merge[key];
        const { r, c, rs, cs } = mergeItem;
        for (let r1 = r; r1 < r + rs; r1++) {
            for (let c1 = c; c1 < c + cs; c1++) {
                if (file.data[r1][c1]) {
                    if (file.data[r1][c1].mc && file.data[r1][c1].mc.r === r && file.data[r1][c1].mc.c === c) {
                        delete file.data[r1][c1].mc;
                    }
                }
            }
        }
        delete merge[key];
    });
    const shiftedMatrix = data.map((row) => [...row]);

    let startY = row[0], height = row[1] - row[0] + 1, startX = column[0], width = column[1] - column[0] + 1;
    // 开始进行偏移
    let yEnd = startY + height;
    let xEnd = startX + width;
    let targetX = startX;
    let targetY = startY;
    if (direction === "up") {
        startY -= offset;
        targetY -= offset;
    } else if (direction === "down") {
        yEnd += offset;
        targetY += offset;
    } else if (direction === "left") {
        startX -= offset;
        targetX -= offset;
    } else if (direction === "right") {
        xEnd += offset;
        targetX += offset;
    }
    for (let y = startY; y < yEnd; y++) {
        for (let x = startX; x < xEnd; x++) {
            if (x >= targetX && x < targetX + width && y >= targetY && y < targetY + height) {
                let sourceRow = y, sourceCol = x;

                if (direction === "up") {
                    sourceRow = y + offset;
                } else if (direction === "down") {
                    sourceRow = y - offset;
                } else if (direction === "left") {
                    sourceCol = x + offset;
                } else if (direction === "right") {
                    sourceCol = x - offset;
                }
                shiftedMatrix[y][x] = cloneDeep(data[sourceRow][sourceCol]);
                if (data[sourceRow][sourceCol] && data[sourceRow][sourceCol].f) {
                    // file.calcChain = file.calcChain.filter((calc) => calc.r !== sourceRow && calc.c !== sourceCol);
                    let calc = file.calcChain.find((obj) => obj.r === sourceRow && obj.c === sourceCol);
                    if (calc) {
                        calc.r = y;
                        calc.c = x;
                    }
                }
                if (file.config && file.config.merge && file.config.merge[`${sourceRow}_${sourceCol}`]) {
                    file.config.merge[`${y}_${x}`] = {
                        ...file.config.merge[`${sourceRow}_${sourceCol}`],
                        r: y,
                        c: x
                    };
                    delete file.config.merge[`${sourceRow}_${sourceCol}`];
                } else if (data[sourceRow][sourceCol] && data[sourceRow][sourceCol].mc) {
                    shiftedMatrix[y][x].mc = {
                        r: y - sourceRow + data[sourceRow][sourceCol].mc.r,
                        c: x - sourceCol + data[sourceRow][sourceCol].mc.c
                    };
                }
            } else {
                if (shiftedMatrix[y][x] && shiftedMatrix[y][x].f) {
                    file.calcChain = file.calcChain.filter((calc) => calc.r !== y && calc.c !== x);
                }
                shiftedMatrix[y][x] = null; // 或者其他你希望填充的值
            }
        }
    }

    file.data = shiftedMatrix;
    if (order === getCurrentSheetIndex()) {
        Store.flowdata = shiftedMatrix;
        luckysheetrefreshgrid();
    }
}
